diff --git a/go.mod b/go.mod index ed30cc032bdb6d967e55dcf82bef370a4a7e10d0..372a5f1f61d2f523d327d630e73fa21f0aad2eac 100644 --- a/go.mod +++ b/go.mod @@ -62,8 +62,8 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 go.uber.org/multierr v1.11.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.15.0 google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 9a372e53de2bdc61ea06ca757784389c4cb1f383..7f00103fa5b26228e50be943d05c23325cbdc38c 100644 --- a/go.sum +++ b/go.sum @@ -151,8 +151,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index a99f7b53f8a48253ddf620006b10f1ed8cdca97b..1c3567e12756ac434de98a2dd7d25bb9b5f19ccb 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -2,6 +2,8 @@ package schema import ( "context" + "os" + "path/filepath" "reflect" "git.perx.ru/perxis/perxis-go/pkg/errors" @@ -25,6 +27,41 @@ func NewFromField(f *field.Field) *Schema { return &Schema{Field: *f} } +// FromFile инициализирует Рё возвращает объект схемы РёР· файла +// Поддерживаются форматы JSON Рё YAML +func FromFile(path string) (*Schema, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + switch filepath.Ext(path) { + case ".json": + return FromJSON(file) + case ".yaml", ".yml": + return FromYAML(file) + } + + return nil, errors.New("schema must be in JSON or YAML format") +} + +// FromFiles возвращает РІСЃРµ валидные схемы РІ переданной директории +func FromFiles(path string) ([]*Schema, error) { + files, err := os.ReadDir(path) + if err != nil { + return nil, err + } + + result := make([]*Schema, 0, len(files)) + for _, file := range files { + if schema, err := FromFile(filepath.Join(path, file.Name())); err == nil { + result = append(result, schema) + } + } + return result, nil +} + var ( Encode = field.Encode Decode = field.Decode diff --git a/pkg/schema/schema_json.go b/pkg/schema/schema_json.go index e8710f76dfb8a5a81da279e4ba6d46f77a1bbdb8..2253923067a0dce2ab47a9e8055e6bc1562fb526 100644 --- a/pkg/schema/schema_json.go +++ b/pkg/schema/schema_json.go @@ -1,10 +1,23 @@ package schema import ( + "io" + "git.perx.ru/perxis/perxis-go/pkg/errors" jsoniter "github.com/json-iterator/go" ) +func FromJSON(r io.Reader) (s *Schema, err error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + s = New() + err = s.UnmarshalJSON(data) + return s, err +} + type jsonSchema struct { Loaded bool `json:"loaded"` Metadata map[string]string `json:"metadata"` @@ -23,15 +36,6 @@ func (s *Schema) UnmarshalJSON(b []byte) error { return err } - //if len(j.Field) > 0 { - // if err := s.Field.UnmarshalJSON(j.Field); err != nil { - // return err - // } - // //if err := jsoniter.Unmarshal(j.Field, &s.Field); err != nil { - // // return err - // //} - //} - return nil } diff --git a/pkg/schema/schema_yaml.go b/pkg/schema/schema_yaml.go new file mode 100644 index 0000000000000000000000000000000000000000..7bfe6365ca648649ce353e8534aead2d4f2f2419 --- /dev/null +++ b/pkg/schema/schema_yaml.go @@ -0,0 +1,55 @@ +package schema + +import ( + "io" + + jsoniter "github.com/json-iterator/go" + "gopkg.in/yaml.v3" +) + +func FromYAML(r io.Reader) (s *Schema, err error) { + yml, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + s = New() + err = s.UnmarshalYAML(yml) + return s, err +} + +func (s *Schema) UnmarshalYAML(b []byte) error { + jsonData, err := yamlToJson(b) + if err != nil { + return err + } + + return s.UnmarshalJSON(jsonData) +} + +func (s *Schema) MarshalYAML() ([]byte, error) { + jsonData, err := s.MarshalJSON() + if err != nil { + return nil, err + } + + return jsonToYaml(jsonData) +} + +func jsonToYaml(b []byte) ([]byte, error) { + var data interface{} + if err := jsoniter.Unmarshal(b, &data); err != nil { + return nil, err + } + + return yaml.Marshal(data) +} + +func yamlToJson(b []byte) ([]byte, error) { + var data interface{} + if err := yaml.Unmarshal(b, &data); err != nil { + return nil, err + } + + return jsoniter.Marshal(data) +} diff --git a/pkg/schema/test/assets/invalid.json b/pkg/schema/test/assets/invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pkg/schema/test/assets/not_schema.txt b/pkg/schema/test/assets/not_schema.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pkg/schema/test/assets/web_pages.json b/pkg/schema/test/assets/web_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..652753c911e79dca0896b2c57587400202d0da69 --- /dev/null +++ b/pkg/schema/test/assets/web_pages.json @@ -0,0 +1,420 @@ +{ + "ui": { + "widget": "Tabs", + "options": { + "description": "path", + "collection_icon": "ApartmentOutlined/FileTextOutlined", + "fields": [ + "content", + "seo", + "settings", + "advanced", + "design", + "variables" + ], + "title": "name", + "key": "path" + }, + "list_view": { + "options": { + "sort": [ + "path" + ], + "page_size": 50, + "fields": [ + "path", + "name", + "updated_at", + "updated_by", + "state" + ] + } + } + }, + "includes": [ + { + "ref": "hoop_item_options", + "optional": true + }, + { + "ref": "ext_web_pages_*", + "optional": true + } + ], + "type": "object", + "params": { + "inline": false, + "fields": { + "design": { + "title": "Дизайн", + "includes": [ + { + "ref": "web_design" + } + ], + "type": "object", + "params": { + "inline": false, + "fields": {} + } + }, + "variables": { + "title": "Переменные", + "ui": { + "options": { + "fields": [ + "variables" + ] + } + }, + "type": "object", + "params": { + "inline": true, + "fields": { + "variables": { + "type": "array", + "params": { + "item": { + "ui": { + "options": { + "collection_icon": "SettingOutlined/FileExcelOutlined", + "fields": [ + "id", + "name", + "value" + ] + }, + "list_view": { + "options": { + "fields": [ + "id", + "name", + "updated_at", + "updated_by", + "state" + ], + "page_size": 50 + } + } + }, + "type": "object", + "params": { + "inline": false, + "fields": { + "id": { + "title": "Рдентификатор переменной", + "text_search": true, + "options": { + "required": true + }, + "type": "string", + "params": {} + }, + "name": { + "title": "Название переменной", + "text_search": true, + "type": "string", + "params": {} + }, + "value": { + "title": "Значение переменной", + "type": "string", + "params": {} + } + } + } + } + } + } + } + } + }, + "content": { + "title": "Содержимое страницы", + "ui": { + "options": { + "fields": [ + "blocks" + ] + } + }, + "type": "object", + "params": { + "inline": true, + "fields": { + "blocks": { + "title": "Блоки", + "ui": { + "widget": "BlockList", + "options": { + "create": { + "classes": [ + "class_web_blocks" + ] + } + } + }, + "type": "array", + "params": { + "item": { + "title": "Блок", + "type": "reference", + "params": { + "allowedCollections": [ + "web_block_*" + ] + } + } + } + } + } + } + }, + "seo": { + "title": "SEO", + "includes": [ + { + "ref": "web_seo" + } + ], + "type": "object", + "params": { + "inline": false, + "fields": {} + } + }, + "settings": { + "title": "Настройки", + "ui": { + "options": { + "fields": [ + "name", + "parent", + "path", + "slug", + "section", + "datasource", + "redirect", + "redirect_path", + "redirect_url", + "outputs", + "navTitle", + "navHide", + "weight" + ] + } + }, + "type": "object", + "params": { + "inline": true, + "fields": { + "path": { + "title": "Путь", + "unique": true, + "text_search": true, + "options": { + "value": "$perxis.Item.Template == true ? _value : parent == nil ? '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))" + }, + "type": "string", + "params": {} + }, + "redirect_url": { + "title": "URL для перехода", + "condition": "redirect == true", + "type": "string", + "params": {} + }, + "outputs": { + "title": "Форматы страницы для вывода", + "type": "array", + "params": { + "item": { + "ui": { + "widget": "Lookup", + "options": { + "allowedCollections": [ + { + "collection": "web_outputs" + } + ] + } + }, + "type": "string", + "params": {} + } + } + }, + "weight": { + "title": "РџРѕСЂСЏРґРѕРє следования", + "ui": { + "widget": "NumberInput" + }, + "type": "number", + "params": { + "format": "int" + } + }, + "navHide": { + "title": "Скрыть страницу РёР· навигации", + "ui": { + "widget": "Checkbox" + }, + "type": "bool", + "params": {} + }, + "section": { + "title": "Раздел", + "ui": { + "widget": "Checkbox" + }, + "type": "bool", + "params": {} + }, + "parent": { + "title": "Родительский раздел", + "description": "Раздел сайта, РіРґРµ расположена страница", + "ui": { + "widget": "Lookup", + "options": { + "allowedCollections": [ + { + "collection": "web_pages" + } + ] + } + }, + "type": "string", + "params": {} + }, + "name": { + "title": "Название", + "indexed": true, + "text_search": true, + "options": { + "required": true, + "value": "$perxis.Item.Template == true ? _value : replace_markers(_value)" + }, + "type": "string", + "params": {} + }, + "navTitle": { + "title": "Название для навигации", + "type": "string", + "params": {} + }, + "redirect_path": { + "title": "Страница для перехода", + "ui": { + "widget": "Lookup", + "options": { + "allowedCollections": [ + { + "collection": "web_pages" + } + ] + } + }, + "condition": "redirect == true", + "type": "string", + "params": {} + }, + "datasource": { + "title": "Рсточник данных", + "description": "Рсточник данных РёР· которого Р±СѓРґСѓС‚ формироваться подстраницы раздела", + "ui": { + "widget": "Lookup", + "options": { + "allowedCollections": [ + { + "collection": "web_datasources" + } + ] + } + }, + "condition": "section == true", + "type": "string", + "params": {} + }, + "redirect": { + "title": "Перенаправление", + "description": "Страница РЅРµ имеет содержимого Рё перенаправляет посетителей РЅР° РґСЂСѓРіРѕР№ адрес", + "ui": { + "widget": "Checkbox" + }, + "type": "bool", + "params": {} + }, + "slug": { + "title": "Slug", + "description": "Рдентификатор страницы РІ адресе URL", + "text_search": true, + "options": { + "value": "$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ? slugify(replace_markers(name)) : slugify(replace_markers(_value))" + }, + "type": "string", + "params": {} + } + } + } + }, + "advanced": { + "title": "Расширенные настройки", + "ui": { + "options": { + "fields": [ + "scripts" + ] + } + }, + "type": "object", + "params": { + "inline": false, + "fields": { + "scripts": { + "title": "Дополнительные скрипты", + "type": "array", + "params": { + "item": { + "ui": { + "options": { + "fields": [ + "src", + "type", + "content" + ] + } + }, + "type": "object", + "params": { + "inline": false, + "fields": { + "type": { + "title": "Media Type скрипта", + "type": "string", + "params": {} + }, + "content": { + "title": "Содержимое скрипта", + "type": "string", + "params": {} + }, + "src": { + "title": "URL для загрузки скрипта", + "type": "string", + "params": {} + } + } + } + } + } + } + } + } + } + } + }, + "loaded": false, + "metadata": { + "extension": "perxisweb" + } +} \ No newline at end of file diff --git a/pkg/schema/test/assets/web_pages.yml b/pkg/schema/test/assets/web_pages.yml new file mode 100644 index 0000000000000000000000000000000000000000..7fdfdd24fa9dcaa04d31ca1506955c86d6e3ce43 --- /dev/null +++ b/pkg/schema/test/assets/web_pages.yml @@ -0,0 +1,297 @@ +--- +ui: + widget: Tabs + options: + description: path + collection_icon: ApartmentOutlined/FileTextOutlined + fields: + - content + - seo + - settings + - advanced + - design + - variables + title: name + key: path + list_view: + options: + sort: + - path + page_size: 50 + fields: + - path + - name + - updated_at + - updated_by + - state +includes: +- ref: hoop_item_options + optional: true +- ref: ext_web_pages_* + optional: true +type: object +params: + inline: false + fields: + design: + title: Дизайн + includes: + - ref: web_design + type: object + params: + inline: false + fields: {} + variables: + title: Переменные + ui: + options: + fields: + - variables + type: object + params: + inline: true + fields: + variables: + type: array + params: + item: + ui: + options: + collection_icon: SettingOutlined/FileExcelOutlined + fields: + - id + - name + - value + list_view: + options: + fields: + - id + - name + - updated_at + - updated_by + - state + page_size: 50 + type: object + params: + inline: false + fields: + id: + title: Рдентификатор переменной + text_search: true + options: + required: true + type: string + params: {} + name: + title: Название переменной + text_search: true + type: string + params: {} + value: + title: Значение переменной + type: string + params: {} + content: + title: Содержимое страницы + ui: + options: + fields: + - blocks + type: object + params: + inline: true + fields: + blocks: + title: Блоки + ui: + widget: BlockList + options: + create: + classes: + - class_web_blocks + type: array + params: + item: + title: Блок + type: reference + params: + allowedCollections: + - web_block_* + seo: + title: SEO + includes: + - ref: web_seo + type: object + params: + inline: false + fields: {} + settings: + title: Настройки + ui: + options: + fields: + - name + - parent + - path + - slug + - section + - datasource + - redirect + - redirect_path + - redirect_url + - outputs + - navTitle + - navHide + - weight + type: object + params: + inline: true + fields: + path: + title: Путь + unique: true + text_search: true + options: + value: "$perxis.Item.Template == true ? _value : parent == nil ? '/' + : slug == nil ? make_path(parent, slugify(replace_markers(name))) + : make_path(parent, slugify(replace_markers(slug)))" + type: string + params: {} + redirect_url: + title: URL для перехода + condition: redirect == true + type: string + params: {} + outputs: + title: Форматы страницы для вывода + type: array + params: + item: + ui: + widget: Lookup + options: + allowedCollections: + - collection: web_outputs + type: string + params: {} + weight: + title: РџРѕСЂСЏРґРѕРє следования + ui: + widget: NumberInput + type: number + params: + format: int + navHide: + title: Скрыть страницу РёР· навигации + ui: + widget: Checkbox + type: bool + params: {} + section: + title: Раздел + ui: + widget: Checkbox + type: bool + params: {} + parent: + title: Родительский раздел + description: Раздел сайта, РіРґРµ расположена страница + ui: + widget: Lookup + options: + allowedCollections: + - collection: web_pages + type: string + params: {} + name: + title: Название + indexed: true + text_search: true + options: + required: true + value: "$perxis.Item.Template == true ? _value : replace_markers(_value)" + type: string + params: {} + navTitle: + title: Название для навигации + type: string + params: {} + redirect_path: + title: Страница для перехода + ui: + widget: Lookup + options: + allowedCollections: + - collection: web_pages + condition: redirect == true + type: string + params: {} + datasource: + title: Рсточник данных + description: Рсточник данных РёР· которого Р±СѓРґСѓС‚ формироваться подстраницы + раздела + ui: + widget: Lookup + options: + allowedCollections: + - collection: web_datasources + condition: section == true + type: string + params: {} + redirect: + title: Перенаправление + description: Страница РЅРµ имеет содержимого Рё перенаправляет посетителей + РЅР° РґСЂСѓРіРѕР№ адрес + ui: + widget: Checkbox + type: bool + params: {} + slug: + title: Slug + description: Рдентификатор страницы РІ адресе URL + text_search: true + options: + value: "$perxis.Item.Template == true ? _value : parent == nil ? nil + : _value == nil ? slugify(replace_markers(name)) : slugify(replace_markers(_value))" + type: string + params: {} + advanced: + title: Расширенные настройки + ui: + options: + fields: + - scripts + type: object + params: + inline: false + fields: + scripts: + title: Дополнительные скрипты + type: array + params: + item: + ui: + options: + fields: + - src + - type + - content + type: object + params: + inline: false + fields: + type: + title: Media Type скрипта + type: string + params: {} + content: + title: Содержимое скрипта + type: string + params: {} + src: + title: URL для загрузки скрипта + type: string + params: {} +loaded: false +metadata: + extension: perxisweb diff --git a/pkg/schema/test/convert_test.go b/pkg/schema/test/convert_test.go new file mode 100644 index 0000000000000000000000000000000000000000..51ab33a6751440672c8a9a0dc0038f11898749b2 --- /dev/null +++ b/pkg/schema/test/convert_test.go @@ -0,0 +1,211 @@ +package test + +import ( + "testing" + + "git.perx.ru/perxis/perxis-go/pkg/extension" + "git.perx.ru/perxis/perxis-go/pkg/references" + "git.perx.ru/perxis/perxis-go/pkg/schema" + "git.perx.ru/perxis/perxis-go/pkg/schema/field" + "git.perx.ru/perxis/perxis-go/pkg/schema/modify" + "git.perx.ru/perxis/perxis-go/pkg/schema/validate" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFromFile(t *testing.T) { + for _, tt := range []struct { + path string + wantSchema *schema.Schema + wantErr string + }{ + { + path: "assets/not_schema.txt", + wantErr: "schema must be in JSON or YAML format", + }, + { + path: "non/existent.json", + wantErr: "open non/existent.json: no such file or directory", + }, + { + path: "assets/invalid.json", + wantErr: "error unmarshal json into field", + }, + { + path: "assets/web_pages.json", + wantSchema: getPagesSchema(), + }, + { + path: "assets/web_pages.yml", + wantSchema: getPagesSchema(), + }, + } { + t.Run(tt.path, func(t *testing.T) { + result, err := schema.FromFile(tt.path) + if tt.wantErr != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + require.Equal(t, tt.wantSchema, result) + }) + } +} + +func TestFromFiles(t *testing.T) { + t.Run("Non-existen path", func(t *testing.T) { + schemas, err := schema.FromFiles("non-existen") + require.Error(t, err) + require.ErrorContains(t, err, "no such file or directory") + require.Nil(t, schemas) + }) + + t.Run("Success", func(t *testing.T) { + schemas, err := schema.FromFiles("assets") + require.NoError(t, err) + require.Len(t, schemas, 2, "Р’ директории хранятся РґРІРµ корректные схемы") + require.Equal(t, schemas[0], schemas[1], "РћРЅРё одинаковые, РЅРѕ РІ разных форматах") + }) +} + +// Оригинальное объявление схемы Web/Страницы +// Значение констант подставлено вручную +func getPagesSchema() *schema.Schema { + content := field.Object( + true, "blocks", field.Array( + references.Field([]string{"web_block_*"}).SetTitle("Блок"), + ). + SetTitle("Блоки"). + WithUI(&field.UI{ + Widget: "BlockList", + Options: map[string]interface{}{ + "create": map[string]interface{}{ + "classes": []string{"class_web_blocks"}, + }, + }, + }), + ).SetTitle("Содержимое страницы") + + // SEO + seo := field.Object().WithIncludes("web_seo").SetTitle("SEO") + + //Settings + settings := field.Object(true, + "name", field.String( + validate.Required(), + modify.Value("$perxis.Item.Template == true ? _value : replace_markers(_value)"), + ).SetTitle("Название").SetTextSearch(true).SetIndexed(true), + "parent", field.String().SetTitle("Родительский раздел").SetDescription("Раздел сайта, РіРґРµ расположена страница").WithUI(&field.UI{ + Widget: "Lookup", + Options: map[string]interface{}{ + "allowedCollections": []interface{}{ + map[string]interface{}{"collection": "web_pages"}, + }, + }, + }), // TODO lookup "section == true" + "path", field.String( + modify.Value("$perxis.Item.Template == true ? _value : parent == nil ? '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))"), + ).SetTitle("Путь").SetUnique(true).SetTextSearch(true), // TODO readonly + "slug", field.String(modify.Value("$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ? slugify(replace_markers(name)) : slugify(replace_markers(_value))")).SetTitle("Slug").SetDescription("Рдентификатор страницы РІ адресе URL").SetTextSearch(true), + "section", field.Bool().SetTitle("Раздел").WithUI(&field.UI{Widget: "Checkbox"}), + "datasource", field.String().WithUI(&field.UI{ + Widget: "Lookup", + Options: map[string]interface{}{ + "allowedCollections": []interface{}{ + map[string]interface{}{"collection": "web_datasources"}, + }, + }, + }).SetTitle("Рсточник данных").SetDescription("Рсточник данных РёР· которого Р±СѓРґСѓС‚ формироваться подстраницы раздела").SetCondition("section == true"), + "redirect", field.Bool().SetTitle("Перенаправление").SetDescription("Страница РЅРµ имеет содержимого Рё перенаправляет посетителей РЅР° РґСЂСѓРіРѕР№ адрес").WithUI(&field.UI{Widget: "Checkbox"}), + "redirect_path", field.String().SetTitle("Страница для перехода").WithUI(&field.UI{ + Widget: "Lookup", + Options: map[string]interface{}{ + "allowedCollections": []interface{}{ + map[string]interface{}{"collection": "web_pages"}, + }, + }, + }).SetCondition("redirect == true"), + "redirect_url", field.String().SetTitle("URL для перехода").SetCondition("redirect == true"), + "outputs", field.Array(field.String().WithUI(&field.UI{ + Widget: "Lookup", + Options: map[string]interface{}{ + "allowedCollections": []interface{}{ + map[string]interface{}{"collection": "web_outputs"}, + }, + }, + })).SetTitle("Форматы страницы для вывода"), + "navTitle", field.String().SetTitle("Название для навигации"), + "navHide", field.Bool().SetTitle("Скрыть страницу РёР· навигации").WithUI(&field.UI{Widget: "Checkbox"}), + "weight", field.Number(field.NumberFormatInt).SetTitle("РџРѕСЂСЏРґРѕРє следования").WithUI(&field.UI{Widget: "NumberInput"}), + ).SetTitle("Настройки") + + // Advanced + advanced := field.Object( + "scripts", field.Array( + field.Object( + "src", field.String().SetTitle("URL для загрузки скрипта"), + "type", field.String().SetTitle("Media Type скрипта"), + "content", field.String().SetTitle("Содержимое скрипта"), + ), + ).SetTitle("Дополнительные скрипты"), + ).SetTitle("Расширенные настройки") + + // Design + design := field.Object().WithIncludes("web_design").SetTitle("Дизайн") + + //Variables + variables := field.Object(true, "variables", field.Array(getVarsField())).SetTitle("Переменные") + + // Page + page := schema.New( + "content", content, + "seo", seo, + "settings", settings, + "advanced", advanced, + "design", design, + "variables", variables, + ).WithMetadata(extension.MetadataKey, "perxisweb") + + // Includes + page.SetIncludes( + field.Include{Ref: "hoop_item_options", Optional: true}, + field.Include{Ref: "ext_web_pages_*", Optional: true}, + ) + + //UI + page.Field.UI.ListView = &field.View{Options: map[string]interface{}{ + "fields": []string{"path", "name", "updated_at", "updated_by", "state"}, + "sort": []string{"path"}, + "page_size": 50, + }} + page.Field.UI.Options["title"] = "name" + page.Field.UI.Options["key"] = "path" + page.Field.UI.Options["description"] = "path" + page.Field.UI.Widget = "Tabs" + page.Field.UI.Options["collection_icon"] = "ApartmentOutlined/FileTextOutlined" + + _ = page.ConvertTypes() + return page +} + +func getVarsSchema() (sch *schema.Schema) { + sch = schema.New( + "id", field.String(validate.Required()).SetTextSearch(true).SetTitle("Рдентификатор переменной"), + "name", field.String().SetTextSearch(true).SetTitle("Название переменной"), + "value", field.String().SetTitle("Значение переменной"), + ).WithMetadata(extension.MetadataKey, "perxisweb") + //UI + sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{ + "fields": []string{"id", "name", "updated_at", "updated_by", "state"}, + "page_size": 50, + }} + + sch.Field.UI.Options["collection_icon"] = "SettingOutlined/FileExcelOutlined" + return sch +} + +func getVarsField() *field.Field { + return &getVarsSchema().Field +} diff --git a/pkg/schema/test/object_test.go b/pkg/schema/test/object_test.go index f17b64511668ad8b4a86c5d91cb908a781384493..6b53b0ad1993305bce4c6b3797b8c5fe5cffc01c 100644 --- a/pkg/schema/test/object_test.go +++ b/pkg/schema/test/object_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "testing" "time" @@ -292,6 +293,82 @@ func TestSchemaUI_UnmarshalJSON(t *testing.T) { assert.Equal(t, sch, schm) } +func TestSchemaUI_UnmarshalYAML(t *testing.T) { + vw := &field.View{ + Widget: "Widget", + Options: map[string]interface{}{"title": "name", "key": "name"}, + } + ui := &field.UI{ + Widget: "Widget", + Placeholder: "Placeholder", + Options: map[string]interface{}{"title": "name", "key": "name"}, + ListView: vw, + ReadView: vw, + EditView: vw, + } + schm := schema.New( + "name", field.String().WithUI(ui), + ) + schm.UI = ui + + j := ` +--- +ui: + widget: Widget + placeholder: Placeholder + options: + title: name + key: name + read_view: + widget: Widget + options: + title: name + key: name + edit_view: + widget: Widget + options: + title: name + key: name + list_view: + widget: Widget + options: + title: name + key: name +type: object +params: + inline: false + fields: + name: + ui: + widget: Widget + placeholder: Placeholder + options: + title: name + key: name + read_view: + widget: Widget + options: + title: name + key: name + edit_view: + widget: Widget + options: + title: name + key: name + list_view: + widget: Widget + options: + title: name + key: name + type: string + params: {} +loaded: false +` + sch, err := schema.FromYAML(strings.NewReader(j)) + require.NoError(t, err) + assert.Equal(t, sch, schm) +} + func TestSchema_GetField(t *testing.T) { sch := schema.New(