Skip to content
Snippets Groups Projects
Commit 0255aad0 authored by Danis Kirasirov's avatar Danis Kirasirov :8ball: Committed by Pavel Antonov
Browse files

feat(schema): Реализован функционал для загрузки схем из файлов

Close #PRXS-2731
parent bb9a96a1
No related branches found
No related tags found
No related merge requests found
...@@ -3,6 +3,7 @@ package collections ...@@ -3,6 +3,7 @@ package collections
import ( import (
"time" "time"
"git.perx.ru/perxis/perxis-go/pkg/optional"
"git.perx.ru/perxis/perxis-go/pkg/permission" "git.perx.ru/perxis/perxis-go/pkg/permission"
"git.perx.ru/perxis/perxis-go/pkg/schema" "git.perx.ru/perxis/perxis-go/pkg/schema"
) )
...@@ -199,3 +200,39 @@ func GetCollectionsIDs(collections []*Collection) []string { ...@@ -199,3 +200,39 @@ func GetCollectionsIDs(collections []*Collection) []string {
} }
return res return res
} }
func FromSchemaMetadata(schemas ...*schema.Schema) []*Collection {
result := make([]*Collection, 0, len(schemas))
for _, sch := range schemas {
coll := &Collection{Schema: sch}
coll.ID = sch.Metadata["collection_id"]
coll.Name = sch.Metadata["collection_name"]
if single, ok := sch.Metadata["collection_single"]; ok && single == "true" {
coll.Single = optional.True
}
if system, ok := sch.Metadata["collection_system"]; ok && system == "true" {
coll.System = optional.True
}
if nodata, ok := sch.Metadata["collection_nodata"]; ok && nodata == "true" {
coll.NoData = optional.True
}
if hidden, ok := sch.Metadata["collection_hidden"]; ok && hidden == "true" {
coll.Hidden = true
}
if _, ok := sch.Metadata["collection_view_id"]; ok {
coll.View = &View{
SpaceID: sch.Metadata["collection_view_space"],
EnvID: sch.Metadata["collection_view_env"],
CollectionID: sch.Metadata["collection_view_id"],
Filter: sch.Metadata["collection_view_filter"],
}
}
result = append(result, coll)
}
return result
}
...@@ -3,6 +3,9 @@ package collections ...@@ -3,6 +3,9 @@ package collections
import ( import (
"testing" "testing"
"git.perx.ru/perxis/perxis-go/pkg/optional"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"git.perx.ru/perxis/perxis-go/pkg/schema/field"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -100,3 +103,84 @@ func TestView_Equal(t *testing.T) { ...@@ -100,3 +103,84 @@ func TestView_Equal(t *testing.T) {
}) })
} }
} }
func TestFromSchemaMetadata(t *testing.T) {
testCases := []struct {
name string
schemas []*schema.Schema
want []*Collection
}{
{
name: "Nil",
schemas: nil,
want: []*Collection{},
},
{
name: "Empty",
schemas: []*schema.Schema{},
want: []*Collection{},
},
{
name: "Without metadata",
schemas: []*schema.Schema{schema.New("a", field.String())},
want: []*Collection{{Schema: schema.New("a", field.String())}},
},
{
name: "With metadata",
schemas: []*schema.Schema{schema.New("a", field.String()).WithMetadata(
"collection_id", "collID",
"collection_name", "collName",
"collection_single", "true",
"collection_system", "true",
"collection_nodata", "true",
"collection_hidden", "true",
"collection_view_space", "viewSpaceID",
"collection_view_env", "viewEnvID",
"collection_view_id", "viewCollID",
"collection_view_filter", "viewFilter",
)},
want: []*Collection{{
ID: "collID",
Name: "collName",
Single: optional.True,
System: optional.True,
NoData: optional.True,
Hidden: true,
Schema: schema.New("a", field.String()).WithMetadata("collection_id", "collID", "collection_name", "collName", "collection_single", "true", "collection_system", "true", "collection_nodata", "true", "collection_hidden", "true", "collection_view_space", "viewSpaceID", "collection_view_env", "viewEnvID", "collection_view_id", "viewCollID", "collection_view_filter", "viewFilter"),
View: &View{
SpaceID: "viewSpaceID",
EnvID: "viewEnvID",
CollectionID: "viewCollID",
Filter: "viewFilter",
},
}},
},
{
name: "Multiple",
schemas: []*schema.Schema{
schema.New("a", field.String()).WithMetadata("collection_id", "collID"),
schema.New("b", field.String()).WithMetadata("collection_name", "collName"),
},
want: []*Collection{
{
ID: "collID",
Name: "",
Schema: schema.New("a", field.String()).WithMetadata("collection_id", "collID"),
},
{
ID: "",
Name: "collName",
Schema: schema.New("b", field.String()).WithMetadata("collection_name", "collName"),
},
},
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
result := FromSchemaMetadata(tt.schemas...)
require.Equal(t, tt.want, result)
})
}
}
...@@ -2,6 +2,7 @@ package extension ...@@ -2,6 +2,7 @@ package extension
import ( import (
"context" "context"
"io/fs"
"git.perx.ru/perxis/perxis-go/pkg/collections" "git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/content" "git.perx.ru/perxis/perxis-go/pkg/content"
...@@ -95,3 +96,22 @@ func UpdateCollectionStrategy(s *setup.Setup, exist, collection *collections.Col ...@@ -95,3 +96,22 @@ func UpdateCollectionStrategy(s *setup.Setup, exist, collection *collections.Col
} }
return setup.DefaultUpdateCollectionStrategyFn(s, exist, collection) return setup.DefaultUpdateCollectionStrategyFn(s, exist, collection)
} }
// CollectionsFromAssets инициализирует коллекции из файловой системы
// В качестве файловой системы можно использовать файлы ОС `filesystem := os.DirFS("assets/schemas")`
// Или файловую систему, встроенную в исполняемый файл при компиляции:
//
// //go:embed assets/schemas/*
// var filesystem embed.FS
func CollectionsFromAssets(filesystem fs.FS, extension string) []*collections.Collection {
schemas, err := schema.FromFS(filesystem)
if err != nil {
panic(err)
}
for _, schema := range schemas {
schema.WithMetadata(MetadataKey, extension)
}
return collections.FromSchemaMetadata(schemas...)
}
...@@ -2,7 +2,7 @@ package schema ...@@ -2,7 +2,7 @@ package schema
import ( import (
"context" "context"
"os" "io/fs"
"path/filepath" "path/filepath"
"reflect" "reflect"
...@@ -29,14 +29,13 @@ func NewFromField(f *field.Field) *Schema { ...@@ -29,14 +29,13 @@ func NewFromField(f *field.Field) *Schema {
// FromFile инициализирует и возвращает объект схемы из файла // FromFile инициализирует и возвращает объект схемы из файла
// Поддерживаются форматы JSON и YAML // Поддерживаются форматы JSON и YAML
func FromFile(path string) (*Schema, error) { func FromFile(file fs.File) (*Schema, error) {
file, err := os.Open(path) stat, err := file.Stat()
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close()
switch filepath.Ext(path) { switch filepath.Ext(stat.Name()) {
case ".json": case ".json":
return FromJSON(file) return FromJSON(file)
case ".yaml", ".yml": case ".yaml", ".yml":
...@@ -46,19 +45,23 @@ func FromFile(path string) (*Schema, error) { ...@@ -46,19 +45,23 @@ func FromFile(path string) (*Schema, error) {
return nil, errors.New("schema must be in JSON or YAML format") return nil, errors.New("schema must be in JSON or YAML format")
} }
// FromFiles возвращает все валидные схемы в переданной директории // FromFS возвращает все валидные схемы в переданной файловой системе
func FromFiles(path string) ([]*Schema, error) { func FromFS(fsys fs.FS) (result []*Schema, err error) {
files, err := os.ReadDir(path) if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error {
file, err := fsys.Open(path)
if err != nil { if err != nil {
return nil, err return err
} }
defer file.Close()
result := make([]*Schema, 0, len(files)) if schema, err := FromFile(file); err == nil {
for _, file := range files {
if schema, err := FromFile(filepath.Join(path, file.Name())); err == nil {
result = append(result, schema) result = append(result, schema)
} }
return nil
}); err != nil {
return nil, err
} }
return result, nil return result, nil
} }
......
package test package test
import ( import (
"os"
"testing" "testing"
"git.perx.ru/perxis/perxis-go/pkg/extension" "git.perx.ru/perxis/perxis-go/pkg/extension"
...@@ -23,10 +24,6 @@ func TestFromFile(t *testing.T) { ...@@ -23,10 +24,6 @@ func TestFromFile(t *testing.T) {
path: "assets/not_schema.txt", path: "assets/not_schema.txt",
wantErr: "schema must be in JSON or YAML format", 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", path: "assets/invalid.json",
wantErr: "error unmarshal json into field", wantErr: "error unmarshal json into field",
...@@ -41,7 +38,10 @@ func TestFromFile(t *testing.T) { ...@@ -41,7 +38,10 @@ func TestFromFile(t *testing.T) {
}, },
} { } {
t.Run(tt.path, func(t *testing.T) { t.Run(tt.path, func(t *testing.T) {
result, err := schema.FromFile(tt.path) file, err := os.Open(tt.path)
require.NoError(t, err)
result, err := schema.FromFile(file)
if tt.wantErr != "" { if tt.wantErr != "" {
require.Error(t, err) require.Error(t, err)
assert.ErrorContains(t, err, tt.wantErr) assert.ErrorContains(t, err, tt.wantErr)
...@@ -56,14 +56,14 @@ func TestFromFile(t *testing.T) { ...@@ -56,14 +56,14 @@ func TestFromFile(t *testing.T) {
func TestFromFiles(t *testing.T) { func TestFromFiles(t *testing.T) {
t.Run("Non-existen path", func(t *testing.T) { t.Run("Non-existen path", func(t *testing.T) {
schemas, err := schema.FromFiles("non-existen") schemas, err := schema.FromFS(os.DirFS("non-existen"))
require.Error(t, err) require.Error(t, err)
require.ErrorContains(t, err, "no such file or directory") require.ErrorContains(t, err, "no such file or directory")
require.Nil(t, schemas) require.Nil(t, schemas)
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
schemas, err := schema.FromFiles("assets") schemas, err := schema.FromFS(os.DirFS("assets"))
require.NoError(t, err) require.NoError(t, err)
require.Len(t, schemas, 2, "В директории хранятся две корректные схемы") require.Len(t, schemas, 2, "В директории хранятся две корректные схемы")
require.Equal(t, schemas[0], schemas[1], "Они одинаковые, но в разных форматах") require.Equal(t, schemas[0], schemas[1], "Они одинаковые, но в разных форматах")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment