From 5a0f936dddbb0f0e94ffb09c37f829045b3cf07a Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Thu, 20 Apr 2023 07:02:37 +0300 Subject: [PATCH 01/20] parse and generate procedure --- internal/pkg/ds/app.go | 21 +++ internal/pkg/ds/package.go | 14 ++ internal/pkg/generator/generator.go | 58 ++++---- internal/pkg/generator/octopus_b_test.go | 74 +++++++++- internal/pkg/generator/tmpl/octopus/main.tmpl | 130 ++++++++++++++---- internal/pkg/generator/tmpl/octopus/mock.tmpl | 20 +-- internal/pkg/parser/field.go | 75 ++++++++++ internal/pkg/parser/import_w_test.go | 2 + internal/pkg/parser/index.go | 71 ++++++++++ internal/pkg/parser/parser.go | 98 +++++++------ internal/pkg/parser/parser_b_test.go | 91 +++++++++++- internal/pkg/parser/parser_w_test.go | 6 +- internal/pkg/parser/utils.go | 7 +- internal/pkg/parser/utils_w_test.go | 4 +- 14 files changed, 560 insertions(+), 111 deletions(-) diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index eb40726..5c24cb4 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -56,6 +56,7 @@ func (i *AppInfo) String() string { // Структура для описания неймспейса сущности type NamespaceDeclaration struct { + ObjectName string Num int64 PublicName string PackageName string @@ -86,6 +87,8 @@ type RecordPackage struct { ImportPkgMap map[string]int // Обратный индекс от пакетов к импортам TriggerMap map[string]TriggerDeclaration // Список триггеров используемых в сущности FlagMap map[string]FlagDeclaration // Список флагов используемых в полях сущности + ProcFields []ProcFieldDeclaration // Описание параметров процедуры, важна последовательность + ProcFieldsMap map[string]int // Обратный индекс от имен } // Конструктор для RecordPackage, инициализирует ссылочные типы @@ -106,6 +109,8 @@ func NewRecordPackage() *RecordPackage { SerializerMap: map[string]SerializerDeclaration{}, TriggerMap: map[string]TriggerDeclaration{}, FlagMap: map[string]FlagDeclaration{}, + ProcFields: []ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, } } @@ -123,6 +128,13 @@ type IndexField struct { Order IndexOrder } +// Тип для описания входных параметров при вызове процедуры +type ParametersDeclaration struct { + Name string // Имя параметра + Selector string // Название функции селектора + Fields []int // Список номеров полей +} + // Тип для описания индекса type IndexDeclaration struct { Name string // Имя индекса @@ -147,6 +159,15 @@ type FieldDeclaration struct { ObjectLink string // является ли поле ссылкой на другую сущность } +// Тип описывающий поле процедуры +type ProcFieldDeclaration struct { + Name string // Название поля + Format octopus.Format // формат поля + Type uint8 // тип параметра (IN, OUT, INOUT) + Size int64 // Размер поля, используется только для строковых значений + Serializer []string // Сериализатора для поля +} + // Метод возвращающий имя сериализатора, если он установлен, иначе пустую строку func (f *FieldDeclaration) SerializerName() string { if len(f.Serializer) > 0 { diff --git a/internal/pkg/ds/package.go b/internal/pkg/ds/package.go index bc0e683..0bb0516 100644 --- a/internal/pkg/ds/package.go +++ b/internal/pkg/ds/package.go @@ -34,6 +34,20 @@ func (rc *RecordPackage) AddField(f FieldDeclaration) error { return nil } +// Добавление нового параметра процедуры в результирующий пакет +func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { + // Проверка на то, что имя не дублируется + if _, ex := rc.ProcFieldsMap[f.Name]; ex { + return &arerror.ErrParseTypeFieldDecl{Name: f.Name, FieldType: string(f.Format), Err: arerror.ErrRedefined} + } + + // добавляем поле и не забываем про обратны индекс + rc.ProcFieldsMap[f.Name] = len(rc.ProcFields) + rc.ProcFields = append(rc.ProcFields, f) + + return nil +} + // Добавление нового ссылочного поля func (rc *RecordPackage) AddFieldObject(fo FieldObject) error { if _, ex := rc.FieldsObjectMap[fo.Name]; ex { diff --git a/internal/pkg/generator/generator.go b/internal/pkg/generator/generator.go index 4c983cb..f292a2d 100644 --- a/internal/pkg/generator/generator.go +++ b/internal/pkg/generator/generator.go @@ -29,37 +29,41 @@ const disclaimer string = `// Code generated by argen. DO NOT EDIT. ` type PkgData struct { - ARPkg string - ARPkgTitle string - FieldList []ds.FieldDeclaration - FieldMap map[string]int - FieldObject map[string]ds.FieldObject - LinkedObject map[string]ds.RecordPackage - Server ds.ServerDeclaration - Container ds.NamespaceDeclaration - Indexes []ds.IndexDeclaration - Serializers map[string]ds.SerializerDeclaration - Imports []ds.ImportDeclaration - Triggers map[string]ds.TriggerDeclaration - Flags map[string]ds.FlagDeclaration - AppInfo string + ARPkg string + ARPkgTitle string + FieldList []ds.FieldDeclaration + FieldMap map[string]int + FieldObject map[string]ds.FieldObject + LinkedObject map[string]ds.RecordPackage + ProcFieldList []ds.ProcFieldDeclaration + ProcFieldMap map[string]int + Server ds.ServerDeclaration + Container ds.NamespaceDeclaration + Indexes []ds.IndexDeclaration + Serializers map[string]ds.SerializerDeclaration + Imports []ds.ImportDeclaration + Triggers map[string]ds.TriggerDeclaration + Flags map[string]ds.FlagDeclaration + AppInfo string } func NewPkgData(appInfo string, cl ds.RecordPackage) PkgData { return PkgData{ - ARPkg: cl.Namespace.PackageName, - ARPkgTitle: cl.Namespace.PublicName, - Indexes: cl.Indexes, - FieldList: cl.Fields, - FieldMap: cl.FieldsMap, - FieldObject: cl.FieldsObjectMap, - Server: cl.Server, - Container: cl.Namespace, - Serializers: cl.SerializerMap, - Imports: cl.Imports, - Triggers: cl.TriggerMap, - Flags: cl.FlagMap, - AppInfo: appInfo, + ARPkg: cl.Namespace.PackageName, + ARPkgTitle: cl.Namespace.PublicName, + Indexes: cl.Indexes, + FieldList: cl.Fields, + FieldMap: cl.FieldsMap, + ProcFieldList: cl.ProcFields, + ProcFieldMap: cl.ProcFieldsMap, + FieldObject: cl.FieldsObjectMap, + Server: cl.Server, + Container: cl.Namespace, + Serializers: cl.SerializerMap, + Imports: cl.Imports, + Triggers: cl.TriggerMap, + Flags: cl.FlagMap, + AppInfo: appInfo, } } diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 87b2592..b98f010 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -121,6 +121,78 @@ func TestGenerateOctopus(t *testing.T) { }, }, }, + { + name: "simpleProcPkg", + want: nil, + args: args{ + params: PkgData{ + ARPkg: packageName, + ARPkgTitle: "Foo", + FieldList: []ds.FieldDeclaration{}, + FieldMap: map[string]int{}, + ProcFieldList: []ds.ProcFieldDeclaration{ + { + Name: "Input", + Format: "string", + Type: 1, + Serializer: []string{}, + }, + { + Name: "InputOutput", + Format: "string", + Type: 2, + Serializer: []string{}, + }, + { + Name: "Output", + Format: "string", + Type: 3, + Serializer: []string{}, + }, + }, + ProcFieldMap: map[string]int{}, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, + Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, + Indexes: []ds.IndexDeclaration{{}}, + Serializers: map[string]ds.SerializerDeclaration{}, + Imports: []ds.ImportDeclaration{}, + Triggers: map[string]ds.TriggerDeclaration{}, + Flags: map[string]ds.FlagDeclaration{}, + AppInfo: "", + }, + }, + wantStr: map[string][]string{ + "octopus": { + `Code generated by argen. DO NOT EDIT.`, + `func (obj *Foo) packPk() ([][]byte, error) {`, + `func (obj *Foo) Equal (anotherObjI any) bool {`, + `func (obj *Foo) PrimaryString() string {`, + `func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, limiter activerecord.SelectorLimiter) ([]*Foo, error) {`, + `func (obj *Foo) GetOutput() string {`, + `func (obj *Foo) GetInputOutput() string {`, + `func Call(ctx context.Context, params FooParams) (*Foo, error)`, + `func NewFromBox(ctx context.Context, tuples []octopus.TupleData) ([]*Foo, error) {`, + `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, + `func New(ctx context.Context) *Foo {`, + `func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) {`, + `procName string = "bar"`, + `type Foo struct {`, + `type FooParams struct {`, + `package ` + packageName, + }, + "mock": { + `func (obj *Foo) mockInsertReplace(ctx context.Context, insertMode octopus.InsertMode) []byte {`, + `func (obj *Foo) MockReplace(ctx context.Context) []byte {`, + `func (obj *Foo) MockInsert(ctx context.Context) []byte {`, + `func (obj *Foo) MockInsertOrReplace(ctx context.Context) []byte {`, + `func (obj *Foo) MockUpdate(ctx context.Context) []byte {`, + `func (obj *Foo) RepoSelector(ctx context.Context) (any, error) {`, + //`func SelectByField1MockerLogger(keys [], res FooList) func() (activerecord.MockerLogger, error) {`, + //`func (obj *Foo) MockSelectByField1sRequest(ctx context.Context, keys [], ) []byte {`, + `func (obj *Foo) MockSelectResponse() ([][]byte, error) {`, + }, + }, + }, } for _, tt := range tests { @@ -139,7 +211,7 @@ func TestGenerateOctopus(t *testing.T) { for _, substr := range strs { if !strings.Contains(buff.String(), substr) { - t.Errorf("GenerateOctopus() = %v, want %v", buff.String(), substr) + t.Errorf("GenerateOctopus() %s = %v, want %v", name, buff.String(), substr) } } } diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 0dbc05d..1e560ac 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -26,43 +26,113 @@ import ( {{ $LinkedObject := .LinkedObject }} {{ $flags := .Flags }} {{ $fields := .FieldList }} +{{ $procfields := .ProcFieldList }} + +{{ if $fields }} + type {{ $PublicStructName }} struct { + octopus.BaseField + {{- range $ind, $fstruct := .FieldList -}} + {{ $rtype := $fstruct.Format -}} + {{ $serlen := len $fstruct.Serializer -}} + {{ if ne $serlen 0 -}} + {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end }} + field{{ $fstruct.Name }} {{ $rtype -}} + {{ end }} + } + + type {{ $PublicStructName }}List []*{{ $PublicStructName }} + + const ( + namespace uint32 = {{ .Container.Num }} + cntFields uint32 = {{ len .FieldList }} + {{- range $fieldname, $flag := .Flags -}} + {{ range $i, $flagname := $flag.Flags }} + {{ $fieldname }}{{ $flagname }}Flag = 1 << {{ $i -}} + {{ end -}} + {{ end }} + ) + + {{ if .Triggers.RepairTuple.Params.Defaults -}} + var defaultValue = [][]byte{ + {{- $notfirst := false -}} + {{ range $ind, $fstruct := .FieldList -}} + {{ $packerparam := packerParam $fstruct.Format -}} + {{ if $notfirst }},{{ end -}} + {{ $notfirst = true }} + {{ $packerparam.DefaultValue -}} + {{ end -}} + } + {{- end }} +{{end}} + +// proc struct + +{{ if $procfields }} + type {{ $PublicStructName }} struct { - octopus.BaseField -{{- range $ind, $fstruct := .FieldList -}} - {{ $rtype := $fstruct.Format -}} - {{ $serlen := len $fstruct.Serializer -}} - {{ if ne $serlen 0 -}} - {{ $sname := index $fstruct.Serializer 0 -}} - {{ $serializer := index $serializers $sname -}} - {{ $rtype = $serializer.Type -}} - {{ end }} - field{{ $fstruct.Name }} {{ $rtype -}} +{{- range $ind, $fstruct := .ProcFieldList -}} + {{ $rtype := $fstruct.Format -}} + {{ $ptype := $fstruct.Type -}} + {{ $serlen := len $fstruct.Serializer -}} + {{ if ne $serlen 0 -}} + {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end }} + {{ if ne $ptype 1 -}} + field{{ $fstruct.Name }} {{ $rtype -}} + {{ end }} {{ end }} } -type {{ $PublicStructName }}List []*{{ $PublicStructName }} - const ( - namespace uint32 = {{ .Container.Num }} - cntFields uint32 = {{ len .FieldList }} -{{- range $fieldname, $flag := .Flags -}} - {{ range $i, $flagname := $flag.Flags }} - {{ $fieldname }}{{ $flagname }}Flag = 1 << {{ $i -}} - {{ end -}} + procName string = "{{ .Container.ObjectName }}" + ) + +{{- range $ind, $fstruct := .ProcFieldList -}} +{{ $rtype := $fstruct.Format -}} +{{ $ptype := $fstruct.Type -}} + {{ if ne $ptype 1 -}} + func (obj *{{ $PublicStructName }}) Get{{ $fstruct.Name }}() {{ $rtype }} { + return obj.field{{ $fstruct.Name }} + } + {{ end }} {{ end }} -) -{{ if .Triggers.RepairTuple.Params.Defaults -}} -var defaultValue = [][]byte{ -{{- $notfirst := false -}} -{{ range $ind, $fstruct := .FieldList -}} - {{ $packerparam := packerParam $fstruct.Format -}} - {{ if $notfirst }},{{ end -}} - {{ $notfirst = true }} - {{ $packerparam.DefaultValue -}} -{{ end -}} + +type {{ $PublicStructName }}Params struct { +{{- range $ind, $fstruct := .ProcFieldList -}} + {{ $rtype := $fstruct.Format -}} + {{ $ptype := $fstruct.Type -}} + {{ $serlen := len $fstruct.Serializer -}} + {{ if ne $serlen 0 -}} + {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end }} + {{ if ne $ptype 3 -}} + field{{ $fstruct.Name }} {{ $rtype -}} + {{ end }} +{{ end }} } -{{- end }} + +func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $PublicStructName }}, error) { + c, _ := box(ctx, 0, activerecord.ReplicaInstanceType) + + td, err := octopus.CallLua(ctx, c, procName, args...) + if err != nil { + return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) + } + + return nil +} + +// end proc struct + +{{end}} {{ if eq .Server.Conf "" -}} var boxOption, _ = octopus.NewOptions( @@ -838,6 +908,7 @@ func (obj *{{ $PublicStructName }}) packPk() ([][]byte, error) { return packedPk, nil } +{{ if $fields }} func (obj *{{ $PublicStructName }}) Delete(ctx context.Context) error { logger := activerecord.Logger() metricTimer := activerecord.Metric().Timer("octopus", "{{ $PublicStructName }}") @@ -1104,3 +1175,4 @@ func (obj *{{ $PublicStructName }}) insertReplace(ctx context.Context, insertMod return nil } +{{ end }} \ No newline at end of file diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index 9035a2b..cd9cdca 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -220,15 +220,17 @@ return func() (activerecord.MockerLogger, error){ {{ end }} pks += "}," {{ else }} - {{ $ifield := index $pkind.Fields 0 -}} - {{ $sfield := index $fields $ifield -}} - {{ $packerparam := packerParam $sfield.Format }} - {{- $tostr := $packerparam.ToString }} - {{- $conv := index $tostr 0 }} - {{ if ne $conv " " }} - pks += {{ index $tostr 0 }}r.Primary(){{ index $tostr 1 }} + ",\n" - {{ else }} - pks += "\"" + r.Primary() + "\",\n" + {{ if $pkind.Fields }} + {{ $ifield := index $pkind.Fields 0 -}} + {{ $sfield := index $fields $ifield -}} + {{ $packerparam := packerParam $sfield.Format }} + {{- $tostr := $packerparam.ToString }} + {{- $conv := index $tostr 0 }} + {{ if ne $conv " " }} + pks += {{ index $tostr 0 }}r.Primary(){{ index $tostr 1 }} + ",\n" + {{ else }} + pks += "\"" + r.Primary() + "\",\n" + {{ end }} {{ end }} {{ end }} } diff --git a/internal/pkg/parser/field.go b/internal/pkg/parser/field.go index dffbb31..c99ca43 100644 --- a/internal/pkg/parser/field.go +++ b/internal/pkg/parser/field.go @@ -117,3 +117,78 @@ func ParseFields(dst *ds.RecordPackage, fields []*ast.Field) error { return nil } + +// Функция парсинга тегов полей модели +func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) error { + tagParam, err := splitTag(field, NoCheckFlag, map[TagNameType]ParamValueRule{PrimaryKeyTag: ParamNotNeedValue, UniqueTag: ParamNotNeedValue}) + if err != nil { + return &arerror.ErrParseTypeFieldDecl{Name: newfield.Name, FieldType: string(newfield.Format), Err: err} + } + + if len(tagParam) > 0 { + for _, kv := range tagParam { + switch TagNameType(kv[0]) { + case ProcInputParamTag: + newfield.Type = newfield.Type | 1 + case ProcOutputParamTag: + newfield.Type = newfield.Type | 2 + case SizeTag: + if kv[1] != "" { + size, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + return &arerror.ErrParseTypeFieldTagDecl{Name: newfield.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrParseTagValueInvalid} + } + + newfield.Size = size + } + case SerializerTag: + newfield.Serializer = strings.Split(kv[1], ",") + default: + return &arerror.ErrParseTypeFieldTagDecl{Name: newfield.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrParseTagUnknown} + } + } + } + + return nil +} + +// Функция парсинга полей процедуры +func ParseProcFields(dst *ds.RecordPackage, fields []*ast.Field) error { + for _, field := range fields { + if field.Names == nil || len(field.Names) != 1 { + return &arerror.ErrParseTypeFieldDecl{Err: arerror.ErrNameDeclaration} + } + + newField := ds.ProcFieldDeclaration{ + Name: field.Names[0].Name, + Serializer: []string{}, + } + + switch t := field.Type.(type) { + case *ast.Ident: + newField.Format = octopus.Format(t.String()) + case *ast.ArrayType: + if t.Elt.(*ast.Ident).Name != "byte" { + return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: t.Elt.(*ast.Ident).Name, Err: arerror.ErrParseFieldArrayOfNotByte} + } + + if t.Len == nil { + return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: t.Elt.(*ast.Ident).Name, Err: arerror.ErrParseFieldArrayNotSlice} + } + + return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: t.Elt.(*ast.Ident).Name, Err: arerror.ErrParseFieldBinary} + default: + return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: fmt.Sprintf("%T", t), Err: arerror.ErrUnknown} + } + + if err := ParseProcFieldsTag(field, &newField); err != nil { + return fmt.Errorf("error ParseFieldsTag: %w", err) + } + + if err := dst.AddProcField(newField); err != nil { + return err + } + } + + return nil +} diff --git a/internal/pkg/parser/import_w_test.go b/internal/pkg/parser/import_w_test.go index 0162957..39c5f84 100644 --- a/internal/pkg/parser/import_w_test.go +++ b/internal/pkg/parser/import_w_test.go @@ -42,6 +42,8 @@ func TestParseImport(t *testing.T) { PackageName: "", }, Backends: []string{}, + ProcFields: []ds.ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, diff --git a/internal/pkg/parser/index.go b/internal/pkg/parser/index.go index f270211..42759b8 100644 --- a/internal/pkg/parser/index.go +++ b/internal/pkg/parser/index.go @@ -172,3 +172,74 @@ func ParseIndexes(dst *ds.RecordPackage, fields []*ast.Field) error { return nil } + +func ParseParams(dst *ds.RecordPackage, fields []*ast.Field) error { + if len(fields) == 0 { + return nil + } + + for _, field := range fields { + if field.Names == nil || len(field.Names) != 1 { + return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Err: arerror.ErrNameDeclaration} + } + + ind := ds.IndexDeclaration{ + Name: field.Names[0].Name, + Fields: []int{}, + FieldsMap: map[string]ds.IndexField{}, + Selector: "SelectBy" + field.Names[0].Name} + + if err := checkBoolType(field.Type); err != nil { + return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Name: ind.Name, Err: arerror.ErrTypeNotBool} + } + + if err := ParseParamTag(field, &ind, dst.FieldsMap); err != nil { + return fmt.Errorf("error parse indexTag: %w", err) + } + + if err := dst.AddIndex(ind); err != nil { + return err + } + } + + return nil +} + +func ParseParamTag(field *ast.Field, ind *ds.IndexDeclaration, fieldsMap map[string]int) error { + tagParam, err := splitTag(field, CheckFlagEmpty, map[TagNameType]ParamValueRule{PrimaryKeyTag: ParamNotNeedValue, UniqueTag: ParamNotNeedValue}) + if err != nil { + return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Name: ind.Name, Err: err} + } + + for _, kv := range tagParam { + switch TagNameType(kv[0]) { + case SelectorTag: + ind.Selector = kv[1] + case FieldsTag: + for _, fieldName := range strings.Split(kv[1], ",") { + if fldNum, ex := fieldsMap[fieldName]; !ex { + return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} + } else if _, ex := ind.FieldsMap[fieldName]; ex { + return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrDuplicate} + } else { + ind.FieldsMap[fieldName] = ds.IndexField{IndField: fldNum, Order: ds.IndexOrderAsc} + ind.Fields = append(ind.Fields, fldNum) + } + } + case OrderDescTag: + for _, fn := range strings.Split(kv[1], ",") { + if _, ex := fieldsMap[fn]; !ex { + return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} + } else if indField, ex := ind.FieldsMap[fn]; !ex { + return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} + } else { + indField.Order = ds.IndexOrderDesc + } + } + default: + return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrParseTagUnknown} + } + } + + return nil +} diff --git a/internal/pkg/parser/parser.go b/internal/pkg/parser/parser.go index bf1da3a..27e57a9 100644 --- a/internal/pkg/parser/parser.go +++ b/internal/pkg/parser/parser.go @@ -16,6 +16,7 @@ type StructNameType string const ( Fields StructNameType = "Fields" + ProcFields StructNameType = "ProcFields" FieldsObject StructNameType = "FieldsObject" Indexes StructNameType = "Indexes" IndexParts StructNameType = "IndexParts" @@ -27,14 +28,16 @@ const ( type TagNameType string const ( - SelectorTag TagNameType = "selector" - PrimaryKeyTag TagNameType = "primary_key" - UniqueTag TagNameType = "unique" - MutatorsTag TagNameType = "mutators" - SizeTag TagNameType = "size" - SerializerTag TagNameType = "serializer" - FieldsTag TagNameType = "fields" - OrderDescTag TagNameType = "orderdesc" + SelectorTag TagNameType = "selector" + PrimaryKeyTag TagNameType = "primary_key" + UniqueTag TagNameType = "unique" + MutatorsTag TagNameType = "mutators" + SizeTag TagNameType = "size" + SerializerTag TagNameType = "serializer" + FieldsTag TagNameType = "fields" + OrderDescTag TagNameType = "orderdesc" + ProcInputParamTag TagNameType = "input" + ProcOutputParamTag TagNameType = "output" ) type TypeName string @@ -85,40 +88,22 @@ func parseAst(pkgName string, decls []ast.Decl, rc *ds.RecordPackage) error { return nil } -func parseStructNameType(dst *ds.RecordPackage, nodeName string, curr *ast.StructType) error { - switch StructNameType(nodeName) { - case Fields: - return ParseFields(dst, curr.Fields.List) - case FieldsObject: - return ParseFieldsObject(dst, curr.Fields.List) - case Indexes: - return ParseIndexes(dst, curr.Fields.List) - case IndexParts: - return ParseIndexPart(dst, curr.Fields.List) - case Serializers: - return ParseSerializer(dst, curr.Fields.List) - case Triggers: - return ParseTrigger(dst, curr.Fields.List) - case Flags: - return ParseFlags(dst, curr.Fields.List) - default: - return arerror.ErrUnknown - } -} - func parseStructType(dst *ds.RecordPackage, name string, doc *ast.CommentGroup, curr *ast.StructType) error { nodeName, public, private, err := getNodeName(name) if err != nil { return &arerror.ErrParseGenDecl{Name: name, Err: err} } - if nodeName == "Fields" { - dst.Namespace.PublicName = public - dst.Namespace.PackageName = private - - if err = parseDoc(dst, doc); err != nil { + switch StructNameType(nodeName) { + case Fields: + fallthrough + case ProcFields: + if err = parseDoc(dst, nodeName, doc); err != nil { return err } + + dst.Namespace.PublicName = public + dst.Namespace.PackageName = private } if err = parseStructNameType(dst, nodeName, curr); err != nil { @@ -156,6 +141,29 @@ func parseTokenType(dst *ds.RecordPackage, genD *ast.GenDecl) error { return nil } +func parseStructNameType(dst *ds.RecordPackage, nodeName string, curr *ast.StructType) error { + switch StructNameType(nodeName) { + case Fields: + return ParseFields(dst, curr.Fields.List) + case FieldsObject: + return ParseFieldsObject(dst, curr.Fields.List) + case Indexes: + return ParseIndexes(dst, curr.Fields.List) + case IndexParts: + return ParseIndexPart(dst, curr.Fields.List) + case Serializers: + return ParseSerializer(dst, curr.Fields.List) + case Triggers: + return ParseTrigger(dst, curr.Fields.List) + case Flags: + return ParseFlags(dst, curr.Fields.List) + case ProcFields: + return ParseProcFields(dst, curr.Fields.List) + default: + return arerror.ErrUnknown + } +} + // parseTokenImport финальные проверки перед парсингом импорта func parseTokenImport(dst *ds.RecordPackage, genD *ast.GenDecl) error { for _, spec := range genD.Specs { @@ -190,7 +198,7 @@ func parseGen(dst *ds.RecordPackage, genD *ast.GenDecl) error { } // parseDoc парсинг описания сервеной конфигурации в модели -func parseDoc(dst *ds.RecordPackage, doc *ast.CommentGroup) error { +func parseDoc(dst *ds.RecordPackage, nodeName string, doc *ast.CommentGroup) error { if doc == nil { return arerror.ErrParseDocEmptyBoxDeclaration } @@ -217,13 +225,25 @@ func parseDoc(dst *ds.RecordPackage, doc *ast.CommentGroup) error { } dst.Server.Timeout = timeout + // TODO: привести к общему строковому типу независимому от бекэнда и типа объекта case "namespace": - nsnum, err := strconv.ParseInt(kv[1], 10, 64) - if err != nil { + switch StructNameType(nodeName) { + case Fields: + nsnum, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} + } + + dst.Namespace.Num = nsnum + case ProcFields: + if len(kv[1]) == 0 { + return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} + } + + dst.Namespace.ObjectName = kv[1] + default: return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} } - - dst.Namespace.Num = nsnum case "backend": dst.Backends = strings.Split(kv[1], ",") default: diff --git a/internal/pkg/parser/parser_b_test.go b/internal/pkg/parser/parser_b_test.go index d29bcbd..21f92dc 100644 --- a/internal/pkg/parser/parser_b_test.go +++ b/internal/pkg/parser/parser_b_test.go @@ -131,7 +131,9 @@ type TriggersFoo struct { Params: map[string]bool{"Defaults": true}, }, }, - FlagMap: map[string]ds.FlagDeclaration{}, + FlagMap: map[string]ds.FlagDeclaration{}, + ProcFields: []ds.ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, }, }, } @@ -146,3 +148,90 @@ type TriggersFoo struct { }) } } + +func TestParseProc(t *testing.T) { + tempDirs := testutil.InitTmps() + defer tempDirs.Defer() + + textTestPkg := `package repository + +//ar:serverHost:127.0.0.1;serverPort:11111;serverTimeout:500 +//ar:namespace:bar +//ar:backend:octopus +type ProcFieldsFoo struct { + InParams1 string ` + "`" + `ar:"input"` + "`" + ` + InOutParams2 string ` + "`" + `ar:"input;output"` + "`" + ` + Output string ` + "`" + `ar:"output"` + "`" + ` +} +` + + srcRoot, err := tempDirs.AddTempDir() + if err != nil { + t.Errorf("can't initialize directory: %s", err) + return + } + + src := filepath.Join(srcRoot, "model/repository/decl") + + if err = os.MkdirAll(src, 0755); err != nil { + t.Errorf("prepare test files error: %s", err) + return + } + + if err = os.WriteFile(filepath.Join(src, "foo.go"), []byte(textTestPkg), 0644); err != nil { + t.Errorf("prepare test files error: %s", err) + return + } + + type args struct { + srcFileName string + rc *ds.RecordPackage + } + tests := []struct { + name string + args args + wantErr bool + want *ds.RecordPackage + }{ + { + name: "simple proc decl", + args: args{ + srcFileName: filepath.Join(src, "foo.go"), + rc: ds.NewRecordPackage(), + }, + wantErr: false, + want: &ds.RecordPackage{ + Namespace: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Foo", PackageName: "foo"}, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11111"}, + Fields: []ds.FieldDeclaration{}, + FieldsMap: map[string]int{}, + ProcFields: []ds.ProcFieldDeclaration{ + {Name: "InParams1", Format: "string", Type: 1, Serializer: []string{}}, + {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, + {Name: "Output", Format: "string", Type: 2, Serializer: []string{}}, + }, + ProcFieldsMap: map[string]int{"InOutParams2": 1, "InParams1": 0, "Output": 2}, + FieldsObjectMap: map[string]ds.FieldObject{}, + Indexes: []ds.IndexDeclaration{}, + IndexMap: map[string]int{}, + SelectorMap: map[string]int{}, + Backends: []string{"octopus"}, + SerializerMap: map[string]ds.SerializerDeclaration{}, + Imports: []ds.ImportDeclaration{}, + ImportMap: map[string]int{}, + ImportPkgMap: map[string]int{}, + TriggerMap: map[string]ds.TriggerDeclaration{}, + FlagMap: map[string]ds.FlagDeclaration{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := parser.Parse(tt.args.srcFileName, tt.args.rc); (err != nil) != tt.wantErr { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Check(t, cmp.DeepEqual(tt.want, tt.args.rc), "Invalid response, test `%s`", tt.name) + }) + } +} diff --git a/internal/pkg/parser/parser_w_test.go b/internal/pkg/parser/parser_w_test.go index fa6f749..9b6c88c 100644 --- a/internal/pkg/parser/parser_w_test.go +++ b/internal/pkg/parser/parser_w_test.go @@ -49,6 +49,8 @@ func Test_parseDoc(t *testing.T) { Backends: []string{"octopus"}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, + ProcFields: []ds.ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, Indexes: []ds.IndexDeclaration{}, IndexMap: map[string]int{}, @@ -90,7 +92,7 @@ func Test_parseDoc(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := parseDoc(tt.args.dst, tt.args.docs); (err != nil) != tt.wantErr { + if err := parseDoc(tt.args.dst, string(Fields), tt.args.docs); (err != nil) != tt.wantErr { t.Errorf("parseDoc() error = %v, wantErr %v", err, tt.wantErr) return } @@ -365,6 +367,8 @@ func Test_parseAst(t *testing.T) { want: &ds.RecordPackage{ Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, Namespace: ds.NamespaceDeclaration{Num: 5, PublicName: "Baz", PackageName: "baz"}, + ProcFields: []ds.ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, diff --git a/internal/pkg/parser/utils.go b/internal/pkg/parser/utils.go index 02ada51..df53c26 100644 --- a/internal/pkg/parser/utils.go +++ b/internal/pkg/parser/utils.go @@ -15,6 +15,7 @@ var ToLower = cases.Lower(language.English) var availableNodeName = []StructNameType{ FieldsObject, Fields, + ProcFields, Indexes, IndexParts, Serializers, @@ -93,9 +94,9 @@ func splitParam(str string, rule map[TagNameType]ParamValueRule) ([][]string, er r = rule[NameDefaultRule] } - if r == ParamNeedValue && len(kv) != 2 { - return nil, &arerror.ErrParseTagDecl{Name: kv[0], Err: arerror.ErrParseTagNoValue} - } + //if r == ParamNeedValue && len(kv) != 2 { + // return nil, &arerror.ErrParseTagDecl{Name: kv[0], Err: arerror.ErrParseTagNoValue} + //} if r == ParamNotNeedValue && len(kv) == 2 { return nil, &arerror.ErrParseTagDecl{Name: kv[0], Err: arerror.ErrParseTagWithValue} diff --git a/internal/pkg/parser/utils_w_test.go b/internal/pkg/parser/utils_w_test.go index a8938b3..f0dd1a5 100644 --- a/internal/pkg/parser/utils_w_test.go +++ b/internal/pkg/parser/utils_w_test.go @@ -131,11 +131,13 @@ func Test_splitParam(t *testing.T) { want: [][]string{{"a", "b"}, {"d", "f"}, {"g"}}, }, { - name: "tagflagerr", wantErr: true, + name: "tagflagerr", + wantErr: false, args: args{ str: "a", rule: map[TagNameType]ParamValueRule{}, }, + want: [][]string{{"a"}}, }, { name: "tagflagerr", wantErr: true, From 5c8a7ada1524db2fe1dae846af8d7519abd5fa40 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:20:29 +0300 Subject: [PATCH 02/20] fix generate procedure --- internal/app/argen_w_test.go | 2 ++ internal/pkg/checker/checker.go | 8 ++++++ internal/pkg/checker/checker_w_test.go | 3 ++- internal/pkg/generator/tmpl/octopus/main.tmpl | 25 ++++++++++--------- internal/pkg/generator/tmpl/octopus/mock.tmpl | 6 ++--- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/internal/app/argen_w_test.go b/internal/app/argen_w_test.go index 668fd23..91b9128 100644 --- a/internal/app/argen_w_test.go +++ b/internal/app/argen_w_test.go @@ -679,6 +679,8 @@ type TriggersFoo struct { }, FieldsMap: map[string]int{"Field1": 0, "Field2": 1}, FieldsObjectMap: map[string]ds.FieldObject{}, + ProcFields: []ds.ProcFieldDeclaration{}, + ProcFieldsMap: map[string]int{}, Indexes: []ds.IndexDeclaration{ { Name: "Field1Field2", diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index 0635686..c62872f 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -111,6 +111,14 @@ func checkFields(cl *ds.RecordPackage) error { } } + for _, fld := range cl.ProcFields { + if fld.Type < 1 && fld.Type > 3 { + + } + //TODO: добавить валидацию + primaryFound = true + } + if !primaryFound { return &arerror.ErrCheckPackageIndexDecl{Pkg: cl.Namespace.PackageName, Index: "primary", Err: arerror.ErrIndexNotExist} } diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 913f432..3860626 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -137,7 +137,8 @@ func Test_checkFields(t *testing.T) { name: "empty fields", args: args{ cl: ds.RecordPackage{ - Fields: []ds.FieldDeclaration{}, + Fields: []ds.FieldDeclaration{}, + ProcFields: []ds.ProcFieldDeclaration{}, }, }, wantErr: true, diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 1e560ac..7de820d 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -35,7 +35,7 @@ import ( {{ $rtype := $fstruct.Format -}} {{ $serlen := len $fstruct.Serializer -}} {{ if ne $serlen 0 -}} - {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end }} @@ -68,17 +68,15 @@ import ( {{- end }} {{end}} -// proc struct - {{ if $procfields }} - +// proc struct type {{ $PublicStructName }} struct { {{- range $ind, $fstruct := .ProcFieldList -}} {{ $rtype := $fstruct.Format -}} {{ $ptype := $fstruct.Type -}} {{ $serlen := len $fstruct.Serializer -}} {{ if ne $serlen 0 -}} - {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end }} @@ -109,12 +107,12 @@ type {{ $PublicStructName }}Params struct { {{ $ptype := $fstruct.Type -}} {{ $serlen := len $fstruct.Serializer -}} {{ if ne $serlen 0 -}} - {{ $sname := index $fstruct.Serializiner 0 -}} + {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end }} - {{ if ne $ptype 3 -}} - field{{ $fstruct.Name }} {{ $rtype -}} + {{ if ne $ptype 2 -}} + {{ $fstruct.Name }} {{ $rtype -}} {{ end }} {{ end }} } @@ -122,12 +120,14 @@ type {{ $PublicStructName }}Params struct { func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $PublicStructName }}, error) { c, _ := box(ctx, 0, activerecord.ReplicaInstanceType) - td, err := octopus.CallLua(ctx, c, procName, args...) + td, err := octopus.CallLua(ctx, c, procName) if err != nil { return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) } - return nil + _ = td + + return nil, nil } // end proc struct @@ -602,7 +602,7 @@ func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, lim return nps, nil } - +{{ if $fields }} // indexes {{ $pktype := "" }} {{ $pklenfld := 1 }} {{ $pkind := index .Indexes 0 }} @@ -620,7 +620,7 @@ func (obj *{{ $PublicStructName }}) Primary() {{ $ind.Type }} { {{ $ifield.Name }}: obj.Get{{ $ifield.Name }}(), {{- end }} } - {{ else }} + {{ else }} {{- range $_, $fieldNum := $ind.Fields }} {{- $ifield := index $fields $fieldNum }} return obj.Get{{ $ifield.Name }}() @@ -797,6 +797,7 @@ func {{ $ind.Selector }}(ctx context.Context, key {{ $ind.Type }}{{ if not $ind. {{- end }} } {{ end }} +{{ end }}// end indexes {{ range $name, $fobj := .FieldObject -}} {{ $linkedobj := index $LinkedObject $fobj.ObjectName }} func (obj *{{ $PublicStructName }}) Get{{ $name }}(ctx context.Context) ({{ if not $fobj.Unique }}[]{{ end }}*{{ $linkedobj.Namespace.PackageName }}.{{ $linkedobj.Namespace.PublicName }}, error){ diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index cd9cdca..e817211 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -38,7 +38,7 @@ func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { {{ end }} return tuple, nil } - +{{ if $fields }} //indexes {{ $pktype := "" }} {{ $pklenfld := 1 }} {{ $pkind := index .Indexes 0 }} @@ -112,7 +112,7 @@ func (obj *{{ $PublicStructName }}) Mock{{ $ind.Selector }}sRequest(ctx context. {{ $packparam := printf "key.%s" $sfield.Name -}} {{ $fi := index $fidx $sfield.Name }} {{ $fstruct := index $fields $fi }} - {{ $serlen := len $fstruct.Serializer -}} + {{ $serlen := len $fstruct.Serializer -}} data, err = pack{{ $sfield.Name }}([]byte{}, {{ $packparam }}) if err != nil { log.Fatal(ctx, err) @@ -247,7 +247,7 @@ return func() (activerecord.MockerLogger, error){ } {{ end }} - +{{ end }} //indexes func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, error) { data, err := SelectByPrimary(ctx, obj.Primary()) if err != nil { From 773f54bd0ba857bcd2a0a07ad56dc8aa7c6cbfcf Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:51:00 +0300 Subject: [PATCH 03/20] fix cal proc generator --- internal/pkg/ds/app.go | 11 +- internal/pkg/generator/octopus_b_test.go | 9 +- internal/pkg/generator/tmpl/octopus/main.tmpl | 125 ++++++++++++++++-- 3 files changed, 126 insertions(+), 19 deletions(-) diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 5c24cb4..6d39928 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -159,6 +159,15 @@ type FieldDeclaration struct { ObjectLink string // является ли поле ссылкой на другую сущность } +// Метод возвращающий имя сериализатора, если он установлен, иначе пустую строку +func (f *FieldDeclaration) SerializerName() string { + if len(f.Serializer) > 0 { + return f.Serializer[0] + } + + return "" +} + // Тип описывающий поле процедуры type ProcFieldDeclaration struct { Name string // Название поля @@ -169,7 +178,7 @@ type ProcFieldDeclaration struct { } // Метод возвращающий имя сериализатора, если он установлен, иначе пустую строку -func (f *FieldDeclaration) SerializerName() string { +func (f *ProcFieldDeclaration) SerializerName() string { if len(f.Serializer) > 0 { return f.Serializer[0] } diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index b98f010..e25564a 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -164,20 +164,15 @@ func TestGenerateOctopus(t *testing.T) { wantStr: map[string][]string{ "octopus": { `Code generated by argen. DO NOT EDIT.`, - `func (obj *Foo) packPk() ([][]byte, error) {`, - `func (obj *Foo) Equal (anotherObjI any) bool {`, - `func (obj *Foo) PrimaryString() string {`, - `func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, limiter activerecord.SelectorLimiter) ([]*Foo, error) {`, `func (obj *Foo) GetOutput() string {`, `func (obj *Foo) GetInputOutput() string {`, `func Call(ctx context.Context, params FooParams) (*Foo, error)`, - `func NewFromBox(ctx context.Context, tuples []octopus.TupleData) ([]*Foo, error) {`, - `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, - `func New(ctx context.Context) *Foo {`, + `func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*Foo, error) {`, `func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) {`, `procName string = "bar"`, `type Foo struct {`, `type FooParams struct {`, + `func (obj *FooParams) arrayValues() []string`, `package ` + packageName, }, "mock": { diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 7de820d..8642368 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -79,10 +79,10 @@ type {{ $PublicStructName }} struct { {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} - {{ end }} + {{ end -}} {{ if ne $ptype 1 -}} - field{{ $fstruct.Name }} {{ $rtype -}} - {{ end }} + field{{- $fstruct.Name }} {{ $rtype -}} + {{- end }} {{ end }} } @@ -100,7 +100,6 @@ const ( {{ end }} {{ end }} - type {{ $PublicStructName }}Params struct { {{- range $ind, $fstruct := .ProcFieldList -}} {{ $rtype := $fstruct.Format -}} @@ -111,25 +110,121 @@ type {{ $PublicStructName }}Params struct { {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end }} - {{ if ne $ptype 2 -}} + {{- if ne $ptype 2 -}} {{ $fstruct.Name }} {{ $rtype -}} - {{ end }} + {{- end }} {{ end }} } +func (obj *{{ $PublicStructName }}Params) arrayValues() []string { + return []string{ +{{- range $ind, $fstruct := .ProcFieldList -}} + {{ $ptype := $fstruct.Type -}} + {{- if ne $ptype 2 }} + obj.{{- $fstruct.Name -}}, + {{- end }} +{{- end }} + } +} + func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $PublicStructName }}, error) { - c, _ := box(ctx, 0, activerecord.ReplicaInstanceType) + logger := activerecord.Logger() + ctx = logger.SetLoggerValueToContext(ctx, map[string]interface{}{"LuaProc": procName}) + metricTimer := activerecord.Metric().Timer("octopus", "{{ $PublicStructName }}") + metricErrCnt := activerecord.Metric().ErrorCount("octopus", "{{ $PublicStructName }}") + + metricTimer.Timing(ctx, "call_proc") + + connection, err := box(ctx, 0, activerecord.ReplicaInstanceType) + if err != nil { + metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) + logger.Error(ctx, fmt.Sprintf("Error get box '%s'", err)) + + return nil, err + } - td, err := octopus.CallLua(ctx, c, procName) + tuples, err := octopus.CallLua(ctx, connection, procName, params.arrayValues()...) if err != nil { + metricErrCnt.Inc(ctx, "call_proc", 1) return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) } - _ = td + ret, err := tupleToStruct(ctx, tuples) + if err != nil { + metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) + logger.Error(ctx, "Error in response: ", err) + + return nil, err + } + + metricTimer.Finish(ctx, "call_proc") - return nil, nil + return ret, nil } +func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*{{ $PublicStructName }}, error) { + np := {{ $PublicStructName }}{} + + {{ range $ind, $fstruct := .ProcFieldList -}} + {{ $ptype := $fstruct.Type -}} + {{ if ne $ptype 1 -}} + val{{ $fstruct.Name }}, err := Unpack{{ $fstruct.Name -}}(bytes.NewReader(tuples[{{$ind}} - 1].Data[0])) + if err != nil { + return nil, err + } + + np.field{{ $fstruct.Name }} = val{{ $fstruct.Name }} + {{ end }} + {{ end }} + + return &np, nil +} + +{{ range $ind, $fstruct := .ProcFieldList -}} + {{ $packerparam := packerParam $fstruct.Format -}} + {{ $rtype := $fstruct.Format -}} + {{ $sname := $fstruct.SerializerName -}} + {{ if ne $sname "" -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end -}} +func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) { + var {{ $fstruct.Name }} {{ if ne $packerparam.UnpackType "" }}{{ $packerparam.UnpackType }}{{ else }}{{ $fstruct.Format }}{{ end }} + {{ $isPointer := hasPrefix (printf "%s" $rtype) "*" }} + + err := {{ $packerparam.UnpackFunc }}(r, &{{ $fstruct.Name }}, iproto.ModeDefault) + if err != nil { + errRet = fmt.Errorf("error unpack field {{ $fstruct.Name }} in tuple: '%w'", err) + return + } + + bvar := {{ if ne $packerparam.UnpackConvFunc "" -}} + {{ $packerparam.UnpackConvFunc }}({{ $fstruct.Name }}) + {{ else -}} + {{ $fstruct.Name }} + {{ end -}} + + {{ $underlyingType := trimPrefix (printf "%s" $rtype) "*"}} + {{ if ne $sname "" -}} + {{ $serializer := index $serializers $sname -}} + {{ $serparams := $fstruct.SerializerParams }} + + var svar {{ $underlyingType }} + + err = {{ $serializer.ImportName }}.{{ $serializer.Unmarshaler }}({{ $serparams }}bvar, &svar) + if err != nil { + errRet = fmt.Errorf("error unmarshal field {{ $fstruct.Name }}: %w", err) + return + } + + {{ else -}} + svar := bvar + + {{ end -}} + + return {{ if $isPointer }}&svar{{else}}svar{{end}}, nil +} +{{ end }} // end proc struct {{end}} @@ -218,6 +313,7 @@ func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType return box, nil } +{{ if $fields }} func New(ctx context.Context) *{{ $PublicStructName }} { newObj := {{ $PublicStructName }}{} newObj.BaseField.UpdateOps = []octopus.Ops{} @@ -226,6 +322,7 @@ func New(ctx context.Context) *{{ $PublicStructName }} { return &newObj } +{{ end }} {{- if .Triggers.RepairTuple }} func repairTuple(ctx context.Context, tuple *octopus.TupleData) error { @@ -252,6 +349,7 @@ func repairTuple(ctx context.Context, tuple *octopus.TupleData) error { } {{- end }} +{{ if $fields }} func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*{{ $PublicStructName }}, error) { np := New(ctx) @@ -331,6 +429,8 @@ func NewFromBox(ctx context.Context, tuples []octopus.TupleData) ([]*{{ $PublicS return ret, nil } +{{ end -}} + {{ range $ind, $fstruct := .FieldList -}} {{ $packerparam := packerParam $fstruct.Format -}} {{ $rtype := $fstruct.Format -}} @@ -525,6 +625,7 @@ func (obj *{{ $PublicStructName }}) Is{{ $fstruct.Name }}{{ $flag }}() bool { {{ end -}} +{{ if $fields }} func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, limiter activerecord.SelectorLimiter) ([]*{{ $PublicStructName }}, error) { logger := activerecord.Logger() ctx = logger.SetLoggerValueToContext(ctx, activerecord.ValueLogPrefix{"limiter": limiter.String()}) @@ -602,6 +703,8 @@ func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, lim return nps, nil } +{{ end }} + {{ if $fields }} // indexes {{ $pktype := "" }} {{ $pklenfld := 1 }} @@ -843,6 +946,7 @@ func (obj *{{ $PublicStructName }}) Get{{ $name }}(ctx context.Context) ({{ if n {{ end -}} +{{ if $fields }} func (obj *{{ $PublicStructName }}) Equal (anotherObjI any) bool { anotherObj, ok := anotherObjI.(*{{ $PublicStructName }}) if !ok { @@ -909,7 +1013,6 @@ func (obj *{{ $PublicStructName }}) packPk() ([][]byte, error) { return packedPk, nil } -{{ if $fields }} func (obj *{{ $PublicStructName }}) Delete(ctx context.Context) error { logger := activerecord.Logger() metricTimer := activerecord.Metric().Timer("octopus", "{{ $PublicStructName }}") From d9702e9fa5a96e994c55f8f9d5526ae813230dfa Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:10:20 +0300 Subject: [PATCH 04/20] cal proc mocks and fixtures --- internal/pkg/ds/app.go | 48 ++++++------- internal/pkg/generator/octopus_b_test.go | 30 ++++---- internal/pkg/generator/tmpl/meta.tmpl | 5 ++ .../generator/tmpl/octopus/fixturestore.tmpl | 68 ++++++++++++++++++- internal/pkg/generator/tmpl/octopus/main.tmpl | 51 ++++++++++++-- internal/pkg/generator/tmpl/octopus/mock.tmpl | 52 +++++++++++++- pkg/octopus/testutil.go | 21 ++++++ 7 files changed, 222 insertions(+), 53 deletions(-) diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 6d39928..dfcc1b3 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -148,52 +148,46 @@ type IndexDeclaration struct { Partial bool // Признак того, что индекс частичный } -// Тип описывающий поле в сущности +// Serializer Сериализаторы для поля +type Serializer []string + +// FieldDeclaration Тип описывающий поле в сущности type FieldDeclaration struct { Name string // Название поля Format octopus.Format // формат поля PrimaryKey bool // участвует ли поле в первичном ключе (при изменении таких полей необходимо делать delete + insert вместо update) Mutators []FieldMutator // список мутаторов (атомарных действий на уровне БД) Size int64 // Размер поля, используется только для строковых значений - Serializer []string // Сериализатора для поля + Serializer Serializer // Сериализатора для поля ObjectLink string // является ли поле ссылкой на другую сущность } -// Метод возвращающий имя сериализатора, если он установлен, иначе пустую строку -func (f *FieldDeclaration) SerializerName() string { - if len(f.Serializer) > 0 { - return f.Serializer[0] +// Name возвращает имя сериализатора, если он установлен, иначе пустую строку +func (s Serializer) Name() string { + if len(s) > 0 { + return s[0] } return "" } -// Тип описывающий поле процедуры -type ProcFieldDeclaration struct { - Name string // Название поля - Format octopus.Format // формат поля - Type uint8 // тип параметра (IN, OUT, INOUT) - Size int64 // Размер поля, используется только для строковых значений - Serializer []string // Сериализатора для поля -} - -// Метод возвращающий имя сериализатора, если он установлен, иначе пустую строку -func (f *ProcFieldDeclaration) SerializerName() string { - if len(f.Serializer) > 0 { - return f.Serializer[0] +// Params Параметры передаваемые при сериализации. Используется, когда на уровне декларирования +// известно, что сериализатор/десериализатор требует дополнительных константных значений +func (s Serializer) Params() string { + if len(s) > 1 { + return `"` + strings.Join(s[1:], `", "`) + `", ` } return "" } -// Параметры передаваемые при сериализации. Используется, когда на уровне декларирования -// известно, что сериализатор/десериализатор требует дополнительных константных значений -func (f *FieldDeclaration) SerializerParams() string { - if len(f.Serializer) > 1 { - return `"` + strings.Join(f.Serializer[1:], `", "`) + `", ` - } - - return "" +// ProcFieldDeclaration Тип описывающий поле процедуры +type ProcFieldDeclaration struct { + Name string // Название поля + Format octopus.Format // формат поля + Type uint8 // тип параметра (IN, OUT, INOUT) + Size int64 // Размер поля, используется только для строковых значений + Serializer Serializer // Сериализатора для поля } // Тип и константы описывающие мутаторы для поля diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index e25564a..6323b84 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -147,24 +147,33 @@ func TestGenerateOctopus(t *testing.T) { Name: "Output", Format: "string", Type: 3, - Serializer: []string{}, + Serializer: []string{"s2i"}, }, }, ProcFieldMap: map[string]int{}, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, Indexes: []ds.IndexDeclaration{{}}, - Serializers: map[string]ds.SerializerDeclaration{}, - Imports: []ds.ImportDeclaration{}, - Triggers: map[string]ds.TriggerDeclaration{}, - Flags: map[string]ds.FlagDeclaration{}, - AppInfo: "", + Serializers: map[string]ds.SerializerDeclaration{ + "s2i": { + Name: "Output", + Pkg: "github.com/mailru/activerecord/pkg/serializer", + Type: "int", + ImportName: "serializerOutput", + Marshaler: "OutputMarshal", + Unmarshaler: "OutputUnmarshal", + }, + }, + Imports: []ds.ImportDeclaration{}, + Triggers: map[string]ds.TriggerDeclaration{}, + Flags: map[string]ds.FlagDeclaration{}, + AppInfo: "", }, }, wantStr: map[string][]string{ "octopus": { `Code generated by argen. DO NOT EDIT.`, - `func (obj *Foo) GetOutput() string {`, + `func (obj *Foo) GetOutput() int {`, `func (obj *Foo) GetInputOutput() string {`, `func Call(ctx context.Context, params FooParams) (*Foo, error)`, `func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*Foo, error) {`, @@ -176,14 +185,9 @@ func TestGenerateOctopus(t *testing.T) { `package ` + packageName, }, "mock": { - `func (obj *Foo) mockInsertReplace(ctx context.Context, insertMode octopus.InsertMode) []byte {`, - `func (obj *Foo) MockReplace(ctx context.Context) []byte {`, - `func (obj *Foo) MockInsert(ctx context.Context) []byte {`, - `func (obj *Foo) MockInsertOrReplace(ctx context.Context) []byte {`, - `func (obj *Foo) MockUpdate(ctx context.Context) []byte {`, `func (obj *Foo) RepoSelector(ctx context.Context) (any, error) {`, //`func SelectByField1MockerLogger(keys [], res FooList) func() (activerecord.MockerLogger, error) {`, - //`func (obj *Foo) MockSelectByField1sRequest(ctx context.Context, keys [], ) []byte {`, + `func MockCallRequest(ctx context.Context, params FooParams) []byte {`, `func (obj *Foo) MockSelectResponse() ([][]byte, error) {`, }, }, diff --git a/internal/pkg/generator/tmpl/meta.tmpl b/internal/pkg/generator/tmpl/meta.tmpl index e04fe3b..95b3ed6 100644 --- a/internal/pkg/generator/tmpl/meta.tmpl +++ b/internal/pkg/generator/tmpl/meta.tmpl @@ -47,6 +47,7 @@ var NamespacePackages = NSPackage { {{- end }} }, Indexes: map[string]IndexMeta{ + {{ if $ns.Indexes }} {{- $pk := index $ns.Indexes 0 }} {{- range $num, $ind := $ns.Indexes }} {{- if $ind.Primary }}{{ $pk = $ind }}{{ end }} @@ -55,10 +56,14 @@ var NamespacePackages = NSPackage { Unpacker: func(packedKeys [][][]byte) (any, error) { return {{ $ns.Namespace.PackageName }}.UnpackKeyIndex{{ $ind.Name }}(packedKeys)}, }, {{- end }} + {{- end }} }, PK: IndexMeta{ + {{ if $ns.Indexes }} + {{- $pk := index $ns.Indexes 0 }} Name: "{{ $pk.Name }}", Unpacker: func(packedKeys [][][]byte) (any, error) { return {{ $ns.Namespace.PackageName }}.UnpackKeyIndex{{ $pk.Name }}(packedKeys)}, + {{- end }} }, }, {{ end }} diff --git a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl index 5469a27..7d35552 100644 --- a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl @@ -23,9 +23,71 @@ import ( {{ $PackageName := .ARPkg -}} {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} +{{ $procfields := .ProcFieldList }} {{ $typePK := "" -}} {{ $fieldNamePK := "" -}} +{{ if $procfields }} +var {{$PackageName}}Once sync.Once +var {{$PackageName}}Store map[{{$PackageName}}.{{$PublicStructName}}Params]*{{$PackageName}}.{{$PublicStructName}} + +//go:embed data/{{$PackageName}}.yaml +var {{$PackageName}}Source []byte + +func init{{$PublicStructName}}() { + {{$PackageName}}Once.Do(func() { + fixtures := {{$PackageName}}.UnmarshalFixtures({{$PackageName}}Source) + + {{$PackageName}}Store = map[{{$PackageName}}.{{$PublicStructName}}Params]*{{$PackageName}}.{{$PublicStructName}}{} + for _, f := range fixtures { + if _, ok := {{$PackageName}}Store[f.GetParams()]; ok { + log.Fatalf("{{$PackageName}} fixture with params %v are duplicated", f.GetParams()) + } + {{$PackageName}}Store[f.GetParams()] = f + } + }) +} + +func Get{{$PublicStructName}}ByParams(params {{$PackageName}}.{{$PublicStructName}}Params) *{{$PackageName}}.{{$PublicStructName}} { + init{{$PublicStructName}}() + + res, ex := {{$PackageName}}Store[params] + if !ex { + log.Fatalf("{{$PublicStructName}} fixture with params %v not found", params) + } + + ctx := activerecord.Logger().SetLoggerValueToContext(context.Background(), map[string]interface{}{"Get{{$PublicStructName}}ByParams": params, "FixtureStore": "{{$PackageName}}Store"}) + + activerecord.Logger().Debug(ctx, res) + + return res +} + +type {{ $PublicStructName }}ProcedureMocker struct {} + +func (m {{ $PublicStructName }}ProcedureMocker) ByFixtureParams(ctx context.Context, params {{$PackageName}}.FooParams) octopus.FixtureType { + return m.ByParamsMocks(ctx, params, + []octopus.MockEntities{ + Get{{$PublicStructName}}ByParams(params), + }) +} + +func (m {{ $PublicStructName }}ProcedureMocker) ByParamsMocks(ctx context.Context, params {{$PackageName}}.FooParams, mocks []octopus.MockEntities) octopus.FixtureType { + oft, err := octopus.CreateCallFixture( + func(wsubME []octopus.MockEntities) []byte { + return {{$PackageName}}.MockCallRequest(ctx, params) + }, + mocks, + ) + if err != nil { + activerecord.Logger().Fatal(ctx, fmt.Sprintf("Error create mock by params: %s", err)) + } + + return oft +} +{{ end }} + +{{ if $fields }} {{ range $num, $ind := .Indexes -}} {{ $lenfld := len $ind.Fields -}} {{ if $ind.Primary }} @@ -38,7 +100,6 @@ import ( {{ end }} {{ end }} - {{ range $_, $mockOperation := split ",Update,InsertReplace" "," }} var {{$PackageName}}{{ $mockOperation }}Once sync.Once var {{$PackageName}}{{ $mockOperation }}Store map[{{$typePK}}]*{{$PackageName}}.{{$PublicStructName}} @@ -114,7 +175,7 @@ func Update{{ $PublicStructName }}Fixture({{ $fieldNamePK }} {{ $typePK }}) {{ $ {{- range $_, $fstruct := .FieldList}} {{/* Determine real filed type */}} {{ $rtype := $fstruct.Format -}} - {{ $sname := $fstruct.SerializerName -}} + {{ $sname := $fstruct.Serializer.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} @@ -168,6 +229,7 @@ func (bf {{ $PublicStructName }}BuildableFixture) Build(ctx context.Context) (fx return octopus.CreateUpdateFixture(obj.MockUpdate(ctx), wrappedTrigger), promiseIsUsed } +{{- end }} {{ range $num, $ind := .Indexes -}} {{ $lenfld := len $ind.Fields }} @@ -251,4 +313,4 @@ func (m {{ $PublicStructName }}By{{ $ind.Selector }}Mocker) ByKeysMocks(ctx cont return oft } -{{ end }} +{{ end }} \ No newline at end of file diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 8642368..0205e36 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -71,6 +71,7 @@ import ( {{ if $procfields }} // proc struct type {{ $PublicStructName }} struct { + params {{ $PublicStructName }}Params {{- range $ind, $fstruct := .ProcFieldList -}} {{ $rtype := $fstruct.Format -}} {{ $ptype := $fstruct.Type -}} @@ -86,12 +87,19 @@ type {{ $PublicStructName }} struct { {{ end }} } +type {{ $PublicStructName }}List []*{{ $PublicStructName }} + const ( procName string = "{{ .Container.ObjectName }}" ) {{- range $ind, $fstruct := .ProcFieldList -}} {{ $rtype := $fstruct.Format -}} +{{ $sname := $fstruct.Serializer.Name -}} +{{ if ne $sname "" -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} +{{ end -}} {{ $ptype := $fstruct.Type -}} {{ if ne $ptype 1 -}} func (obj *{{ $PublicStructName }}) Get{{ $fstruct.Name }}() {{ $rtype }} { @@ -116,6 +124,10 @@ type {{ $PublicStructName }}Params struct { {{ end }} } +func (obj *{{ $PublicStructName }}) GetParams() {{ $PublicStructName }}Params { + return obj.params +} + func (obj *{{ $PublicStructName }}Params) arrayValues() []string { return []string{ {{- range $ind, $fstruct := .ProcFieldList -}} @@ -183,11 +195,20 @@ func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*{{ $Public {{ range $ind, $fstruct := .ProcFieldList -}} {{ $packerparam := packerParam $fstruct.Format -}} {{ $rtype := $fstruct.Format -}} - {{ $sname := $fstruct.SerializerName -}} + {{ $sname := $fstruct.Serializer.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end -}} + {{ $ptype := $fstruct.Type -}} + {{ if ne $ptype 1 -}} +func (obj *{{ $PublicStructName }}) Set{{ $fstruct.Name }}({{ $fstruct.Name }} {{ $rtype }}) error { + obj.field{{ $fstruct.Name }} = {{ $fstruct.Name}} + + return nil +} + {{- end }} + func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) { var {{ $fstruct.Name }} {{ if ne $packerparam.UnpackType "" }}{{ $packerparam.UnpackType }}{{ else }}{{ $fstruct.Format }}{{ end }} {{ $isPointer := hasPrefix (printf "%s" $rtype) "*" }} @@ -207,7 +228,7 @@ func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) {{ $underlyingType := trimPrefix (printf "%s" $rtype) "*"}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $fstruct.SerializerParams }} + {{ $serparams := $fstruct.Serializer.Params }} var svar {{ $underlyingType }} @@ -224,6 +245,22 @@ func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) return {{ if $isPointer }}&svar{{else}}svar{{end}}, nil } + +func pack{{ $fstruct.Name }}(w []byte, {{ $fstruct.Name }} {{ $rtype }}) ([]byte, error) { + {{ $bvar := $packerparam.PackConvFunc $fstruct.Name -}} + {{ if ne $sname "" -}} + {{ $serializer := index $serializers $sname -}} + {{ $serparams := $fstruct.Serializer.Params -}} + pvar, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}{{ $bvar }}) + if err != nil { + return nil, fmt.Errorf("error marshal field {{ $fstruct.Name }}: %w", err) + } + {{- else -}} + pvar := {{ $bvar }} + {{- end }} + + return {{ $packerparam.PackFunc }}(w, pvar, iproto.ModeDefault), nil +} {{ end }} // end proc struct @@ -434,7 +471,7 @@ func NewFromBox(ctx context.Context, tuples []octopus.TupleData) ([]*{{ $PublicS {{ range $ind, $fstruct := .FieldList -}} {{ $packerparam := packerParam $fstruct.Format -}} {{ $rtype := $fstruct.Format -}} - {{ $sname := $fstruct.SerializerName -}} + {{ $sname := $fstruct.Serializer.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} @@ -443,7 +480,7 @@ func pack{{ $fstruct.Name }}(w []byte, {{ $fstruct.Name }} {{ $rtype }}) ([]byte {{ $bvar := $packerparam.PackConvFunc $fstruct.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $fstruct.SerializerParams -}} + {{ $serparams := $fstruct.Serializer.Params -}} pvar, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}{{ $bvar }}) if err != nil { return nil, fmt.Errorf("error marshal field {{ $fstruct.Name }}: %w", err) @@ -474,7 +511,7 @@ func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) {{ $underlyingType := trimPrefix (printf "%s" $rtype) "*"}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $fstruct.SerializerParams }} + {{ $serparams := $fstruct.Serializer.Params }} var svar {{ $underlyingType }} @@ -769,7 +806,7 @@ func PackKeyIndex{{ $ind.Name }}(ctx context.Context, keys []{{ $ind.Type }}) ([ {{ if ne $serlen 0 }} {{ $sname := index $sfield.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $sfield.SerializerParams -}} + {{ $serparams := $sfield.Serializer.Params -}} skey, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}{{ $packparam }}) if err != nil { return nil, err @@ -834,7 +871,7 @@ func UnpackKeyIndex{{ $ind.Name }}(packedKeys [][][]byte) ([]{{ $ind.Type }}, er {{ if ne $serlen 0 }} {{ $sname := index $sfield.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $sfield.SerializerParams -}} + {{ $serparams := $sfield.Serializer.Params -}} skey, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}{{ $packparam }}) if err != nil { return nil, err diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index e817211..c5dd8f4 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -17,9 +17,54 @@ import ( {{ $pkgName := .ARPkg }} {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} +{{ $procfields := .ProcFieldList }} {{ $serializers := .Serializers -}} {{ $fidx := .FieldMap }} +{{ if $procfields }} +func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { + tuple := [][]byte{} + + var data []byte + + var err error + +{{ range $ind, $fstruct := $procfields -}} +{{ $ptype := $fstruct.Type -}} + {{ if ne $ptype 1 -}} + data, err = pack{{ $fstruct.Name }}([]byte{}, obj.Get{{ $fstruct.Name }}()) + if err != nil { + return nil, err + } + + tuple = append(tuple, data) + {{- end }} +{{ end }} + return tuple, nil +} + +func MockCallRequest(ctx context.Context, params {{ $PublicStructName }}Params) []byte { + log := activerecord.Logger() + ctx = log.SetLoggerValueToContext(ctx, map[string]interface{}{"MockCallRequest": params, "Proc": "{{ $PublicStructName }}"}) + + return octopus.PackLua(procName, params.arrayValues()...) +} + +func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, error) { + data, err := Call(ctx, obj.params) + if err != nil { + return nil, err + } + + if data == nil { + return nil, fmt.Errorf("call {{ $PublicStructName }} with params %v: %w", obj.params, activerecord.ErrNoData) + } + + return data, err +} +{{- end }} + +{{ if $fields }} func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { tuple := [][]byte{} @@ -83,7 +128,7 @@ func (obj *{{ $PublicStructName }}) Mock{{ $ind.Selector }}Request(ctx context.C {{ $serializer := index $serializers $sname -}} {{ $rtype := $serializer.Type -}} func serialize{{ $sfield.Name}}(ctx context.Context, v{{ $sfield.Name}} {{ $rtype }}) {{ $sfield.Format }} { - v, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $fstruct.SerializerParams }} v{{ $sfield.Name}}) + v, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $fstruct.Serializer.Params }} v{{ $sfield.Name}}) if err != nil { activerecord.Logger().Fatal(ctx, err) } @@ -162,7 +207,7 @@ return func() (activerecord.MockerLogger, error){ {{ if ne $serlen 0 }} {{ $sname := index $sfield.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} - {{ $serparams := $sfield.SerializerParams -}} + {{ $serparams := $sfield.Serializer.Params -}} skey, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}{{ $packparam }}) if err != nil { return activerecord.MockerLogger{}, err @@ -322,4 +367,5 @@ func (obj *{{ $PublicStructName }}) mockInsertReplace(ctx context.Context, inser log.Debug(ctx, fmt.Sprintf("insertReplace packed tuple: '%X'\n", w)) return w -} \ No newline at end of file +} +{{- end }} \ No newline at end of file diff --git a/pkg/octopus/testutil.go b/pkg/octopus/testutil.go index d811161..b7b760d 100644 --- a/pkg/octopus/testutil.go +++ b/pkg/octopus/testutil.go @@ -120,6 +120,27 @@ func CreateInsertOrReplaceFixture(entity MockEntities, reqData []byte, trigger f return oft } +// CreateCallFixture - конструктор фикстур для вызова процедуры +func CreateCallFixture(reqData func(mocks []MockEntities) []byte, respEnt []MockEntities) (FixtureType, error) { + newID := atomic.AddUint32(&fixtureID, 1) + + respByte, err := PackMockResponse(respEnt) + if err != nil { + return FixtureType{}, fmt.Errorf("error prepare fixture response: %s", err) + } + + oft := FixtureType{ + ID: newID, + Msg: RequestTypeCall, + Request: reqData(respEnt), + Response: respByte, + RespObjs: respEnt, + Trigger: nil, + } + + return oft, nil +} + func WrapTriggerWithOnUsePromise(trigger func(types []FixtureType) []FixtureType) (wrappedTrigger func(types []FixtureType) []FixtureType, isUsed func() bool) { used := false From 787c3f63cfd588283cf2f2b68805221eb4e39eaa Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:40:40 +0300 Subject: [PATCH 05/20] generation templates for procedure calls --- internal/app/argen_w_test.go | 1 - internal/pkg/checker/checker.go | 2 +- internal/pkg/checker/checker_w_test.go | 3 +- internal/pkg/ds/app.go | 4 +- internal/pkg/ds/package.go | 11 ++- internal/pkg/generator/generator.go | 64 +++++++------ internal/pkg/generator/octopus_b_test.go | 12 ++- .../pkg/generator/tmpl/octopus/fixture.tmpl | 95 +++++++++++++++++-- .../generator/tmpl/octopus/fixturestore.tmpl | 4 +- internal/pkg/generator/tmpl/octopus/main.tmpl | 62 ++++++------ internal/pkg/generator/tmpl/octopus/mock.tmpl | 7 +- internal/pkg/parser/import_w_test.go | 1 - internal/pkg/parser/parser_b_test.go | 6 +- internal/pkg/parser/parser_w_test.go | 2 - 14 files changed, 178 insertions(+), 96 deletions(-) diff --git a/internal/app/argen_w_test.go b/internal/app/argen_w_test.go index 91b9128..7b5c5cc 100644 --- a/internal/app/argen_w_test.go +++ b/internal/app/argen_w_test.go @@ -679,7 +679,6 @@ type TriggersFoo struct { }, FieldsMap: map[string]int{"Field1": 0, "Field2": 1}, FieldsObjectMap: map[string]ds.FieldObject{}, - ProcFields: []ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, Indexes: []ds.IndexDeclaration{ { diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index c62872f..f0bb78d 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -111,7 +111,7 @@ func checkFields(cl *ds.RecordPackage) error { } } - for _, fld := range cl.ProcFields { + for _, fld := range cl.ProcOutFields { if fld.Type < 1 && fld.Type > 3 { } diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 3860626..913f432 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -137,8 +137,7 @@ func Test_checkFields(t *testing.T) { name: "empty fields", args: args{ cl: ds.RecordPackage{ - Fields: []ds.FieldDeclaration{}, - ProcFields: []ds.ProcFieldDeclaration{}, + Fields: []ds.FieldDeclaration{}, }, }, wantErr: true, diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index dfcc1b3..21409b3 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -87,7 +87,8 @@ type RecordPackage struct { ImportPkgMap map[string]int // Обратный индекс от пакетов к импортам TriggerMap map[string]TriggerDeclaration // Список триггеров используемых в сущности FlagMap map[string]FlagDeclaration // Список флагов используемых в полях сущности - ProcFields []ProcFieldDeclaration // Описание параметров процедуры, важна последовательность + ProcInFields []ProcFieldDeclaration // Описание входных параметров процедуры, важна последовательность + ProcOutFields []ProcFieldDeclaration // Описание выходных параметров процедуры, важна последовательность ProcFieldsMap map[string]int // Обратный индекс от имен } @@ -109,7 +110,6 @@ func NewRecordPackage() *RecordPackage { SerializerMap: map[string]SerializerDeclaration{}, TriggerMap: map[string]TriggerDeclaration{}, FlagMap: map[string]FlagDeclaration{}, - ProcFields: []ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, } } diff --git a/internal/pkg/ds/package.go b/internal/pkg/ds/package.go index 0bb0516..5dd5dea 100644 --- a/internal/pkg/ds/package.go +++ b/internal/pkg/ds/package.go @@ -42,8 +42,15 @@ func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { } // добавляем поле и не забываем про обратны индекс - rc.ProcFieldsMap[f.Name] = len(rc.ProcFields) - rc.ProcFields = append(rc.ProcFields, f) + rc.ProcFieldsMap[f.Name] = len(rc.ProcFieldsMap) + // добавляем поле во входные параметры + if f.Type == 1 || f.Type == 3 { + rc.ProcInFields = append(rc.ProcInFields, f) + } + // добавляем поле в выходные параметры + if f.Type == 2 || f.Type == 3 { + rc.ProcOutFields = append(rc.ProcOutFields, f) + } return nil } diff --git a/internal/pkg/generator/generator.go b/internal/pkg/generator/generator.go index f292a2d..0ced0a6 100644 --- a/internal/pkg/generator/generator.go +++ b/internal/pkg/generator/generator.go @@ -29,41 +29,43 @@ const disclaimer string = `// Code generated by argen. DO NOT EDIT. ` type PkgData struct { - ARPkg string - ARPkgTitle string - FieldList []ds.FieldDeclaration - FieldMap map[string]int - FieldObject map[string]ds.FieldObject - LinkedObject map[string]ds.RecordPackage - ProcFieldList []ds.ProcFieldDeclaration - ProcFieldMap map[string]int - Server ds.ServerDeclaration - Container ds.NamespaceDeclaration - Indexes []ds.IndexDeclaration - Serializers map[string]ds.SerializerDeclaration - Imports []ds.ImportDeclaration - Triggers map[string]ds.TriggerDeclaration - Flags map[string]ds.FlagDeclaration - AppInfo string + ARPkg string + ARPkgTitle string + FieldList []ds.FieldDeclaration + FieldMap map[string]int + FieldObject map[string]ds.FieldObject + LinkedObject map[string]ds.RecordPackage + ProcInFieldList []ds.ProcFieldDeclaration + ProcOutFieldList []ds.ProcFieldDeclaration + ProcFieldMap map[string]int + Server ds.ServerDeclaration + Container ds.NamespaceDeclaration + Indexes []ds.IndexDeclaration + Serializers map[string]ds.SerializerDeclaration + Imports []ds.ImportDeclaration + Triggers map[string]ds.TriggerDeclaration + Flags map[string]ds.FlagDeclaration + AppInfo string } func NewPkgData(appInfo string, cl ds.RecordPackage) PkgData { return PkgData{ - ARPkg: cl.Namespace.PackageName, - ARPkgTitle: cl.Namespace.PublicName, - Indexes: cl.Indexes, - FieldList: cl.Fields, - FieldMap: cl.FieldsMap, - ProcFieldList: cl.ProcFields, - ProcFieldMap: cl.ProcFieldsMap, - FieldObject: cl.FieldsObjectMap, - Server: cl.Server, - Container: cl.Namespace, - Serializers: cl.SerializerMap, - Imports: cl.Imports, - Triggers: cl.TriggerMap, - Flags: cl.FlagMap, - AppInfo: appInfo, + ARPkg: cl.Namespace.PackageName, + ARPkgTitle: cl.Namespace.PublicName, + Indexes: cl.Indexes, + FieldList: cl.Fields, + FieldMap: cl.FieldsMap, + ProcInFieldList: cl.ProcInFields, + ProcOutFieldList: cl.ProcOutFields, + ProcFieldMap: cl.ProcFieldsMap, + FieldObject: cl.FieldsObjectMap, + Server: cl.Server, + Container: cl.Namespace, + Serializers: cl.SerializerMap, + Imports: cl.Imports, + Triggers: cl.TriggerMap, + Flags: cl.FlagMap, + AppInfo: appInfo, } } diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 6323b84..a8f7ea2 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -130,7 +130,7 @@ func TestGenerateOctopus(t *testing.T) { ARPkgTitle: "Foo", FieldList: []ds.FieldDeclaration{}, FieldMap: map[string]int{}, - ProcFieldList: []ds.ProcFieldDeclaration{ + ProcInFieldList: []ds.ProcFieldDeclaration{ { Name: "Input", Format: "string", @@ -143,6 +143,14 @@ func TestGenerateOctopus(t *testing.T) { Type: 2, Serializer: []string{}, }, + }, + ProcOutFieldList: []ds.ProcFieldDeclaration{ + { + Name: "InputOutput", + Format: "string", + Type: 2, + Serializer: []string{}, + }, { Name: "Output", Format: "string", @@ -176,7 +184,7 @@ func TestGenerateOctopus(t *testing.T) { `func (obj *Foo) GetOutput() int {`, `func (obj *Foo) GetInputOutput() string {`, `func Call(ctx context.Context, params FooParams) (*Foo, error)`, - `func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*Foo, error) {`, + `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, `func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) {`, `procName string = "bar"`, `type Foo struct {`, diff --git a/internal/pkg/generator/tmpl/octopus/fixture.tmpl b/internal/pkg/generator/tmpl/octopus/fixture.tmpl index 5168927..0de479e 100644 --- a/internal/pkg/generator/tmpl/octopus/fixture.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixture.tmpl @@ -27,9 +27,92 @@ import ( {{ $LinkedObject := .LinkedObject }} {{ $flags := .Flags }} {{ $fields := .FieldList }} +{{ $procfields := .ProcOutFieldList }} {{ $typePK := "" -}} {{ $fieldNamePK := "" -}} +{{ if $procfields }} +type {{ $PublicStructName }}FTPK struct { + {{- range $ind, $fstruct := .ProcInFieldList }} + {{ $rtype := $fstruct.Format -}} + {{ $fstruct.Name }} {{ $rtype -}} `yaml:"{{ $fstruct.Name | snakeCase -}}"` + {{- end }} +} + +type {{ $PublicStructName }}FT struct { + Params {{ $PublicStructName }}FTPK `yaml:"params"` +{{- range $ind, $fstruct := .ProcOutFieldList -}} + {{ $rtype := $fstruct.Format -}} + {{ $serlen := len $fstruct.Serializer -}} + {{ if ne $serlen 0 -}} + {{ $sname := index $fstruct.Serializer 0 -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end }} + {{ $fstruct.Name }} {{ $rtype -}} `yaml:"{{ $fstruct.Name | snakeCase -}}"` +{{- end }} +} + +func MarshalFixtures(objs []*{{$PublicStructName}}) ([]byte, error) { + fts := make([]{{$PublicStructName}}FT, 0, len(objs)) + for _, obj := range objs { + params := obj.GetParams() + + pk := {{$PublicStructName}}FTPK{ + {{- range $ind, $fstruct := .ProcInFieldList }} + {{$fstruct.Name}}: params.{{$fstruct.Name}}, + {{- end }} + } + fts = append(fts, {{$PublicStructName}}FT{ + Params: pk, + {{- range $ind, $fstruct := .ProcOutFieldList }} + {{$fstruct.Name}}: obj.Get{{$fstruct.Name}}(), + {{- end }} + }) + } + return yaml.Marshal(fts) +} + +func UnmarshalFixtures(source []byte) []*{{$PublicStructName}} { + var fixtures []{{$PublicStructName}}FT + + if err := yaml.Unmarshal(source, &fixtures); err != nil { + log.Fatalf("unmarshal {{$PublicStructName}}FT fixture: %v", err) + } + + objs := make([]*{{$PublicStructName}}, 0, len(fixtures)) + + for _, ft := range fixtures { + + o := New(context.Background()) + o.setParams({{$PublicStructName}}Params{ + {{- range $ind, $fstruct := .ProcInFieldList }} + {{$fstruct.Name}}: ft.Params.{{$fstruct.Name}}, + {{- end }} + }) + + {{- range $ind, $fstruct := .ProcOutFieldList }} + if err := o.Set{{$fstruct.Name}}(ft.{{$fstruct.Name}}); err != nil { + log.Fatalf("can't set value %v to field {{$fstruct.Name}} of {{$PublicStructName}} fixture: %s", ft.{{$fstruct.Name}}, err) + } + {{- end }} + + objs = append(objs, o) + } + + return objs +} +{{ end }} + +func (objs {{$PublicStructName}}List) String() string { + o, err := MarshalFixtures(objs) + if err != nil { + activerecord.Logger().Fatal(context.Background(), err) + } + return string(o) +} + +{{ if $fields }} {{ range $num, $ind := .Indexes -}} {{ $lenfld := len $ind.Fields -}} {{ if $ind.Primary }} @@ -42,7 +125,6 @@ import ( {{ end }} {{ end }} - type {{ $PublicStructName }}FT struct { {{- range $ind, $fstruct := .FieldList -}} {{ $rtype := $fstruct.Format -}} @@ -92,14 +174,6 @@ func UnmarshalFixtures(source []byte) []*{{$PublicStructName}} { return objs } -func (objs {{$PublicStructName}}List) String() string { - o, err := MarshalFixtures(objs) - if err != nil { - activerecord.Logger().Fatal(context.Background(), err) - } - return string(o) -} - {{/* Отдельный тип фикстур, чтобы не было пересечения по PrimaryKey для update, select, delete... фикстур в yaml */}} type {{ $PublicStructName }}UpdateFT struct { {{- range $ind, $fstruct := .FieldList -}} @@ -202,4 +276,5 @@ func SetFixtureUpdateOptions(obj *{{$PublicStructName}}, updateOptions []{{$Pub {{ end }} {{- end }} } -} \ No newline at end of file +} +{{ end }} \ No newline at end of file diff --git a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl index 7d35552..ebffabb 100644 --- a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl @@ -23,7 +23,7 @@ import ( {{ $PackageName := .ARPkg -}} {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} -{{ $procfields := .ProcFieldList }} +{{ $procfields := .ProcOutFieldList }} {{ $typePK := "" -}} {{ $fieldNamePK := "" -}} @@ -58,7 +58,7 @@ func Get{{$PublicStructName}}ByParams(params {{$PackageName}}.{{$PublicStructNam ctx := activerecord.Logger().SetLoggerValueToContext(context.Background(), map[string]interface{}{"Get{{$PublicStructName}}ByParams": params, "FixtureStore": "{{$PackageName}}Store"}) - activerecord.Logger().Debug(ctx, res) + activerecord.Logger().Debug(ctx, {{$PackageName}}.{{$PublicStructName}}List([]*{{$PackageName}}.{{$PublicStructName}}{res})) return res } diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 0205e36..cc8cd17 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -26,7 +26,7 @@ import ( {{ $LinkedObject := .LinkedObject }} {{ $flags := .Flags }} {{ $fields := .FieldList }} -{{ $procfields := .ProcFieldList }} +{{ $procfields := .ProcOutFieldList }} {{ if $fields }} type {{ $PublicStructName }} struct { @@ -72,18 +72,15 @@ import ( // proc struct type {{ $PublicStructName }} struct { params {{ $PublicStructName }}Params -{{- range $ind, $fstruct := .ProcFieldList -}} +{{- range $ind, $fstruct := .ProcOutFieldList }} {{ $rtype := $fstruct.Format -}} - {{ $ptype := $fstruct.Type -}} {{ $serlen := len $fstruct.Serializer -}} {{ if ne $serlen 0 -}} {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end -}} - {{ if ne $ptype 1 -}} field{{- $fstruct.Name }} {{ $rtype -}} - {{- end }} {{ end }} } @@ -91,36 +88,31 @@ type {{ $PublicStructName }}List []*{{ $PublicStructName }} const ( procName string = "{{ .Container.ObjectName }}" + cntOutFields uint32 = {{ len .ProcOutFieldList }} ) -{{- range $ind, $fstruct := .ProcFieldList -}} +{{- range $ind, $fstruct := .ProcOutFieldList -}} {{ $rtype := $fstruct.Format -}} {{ $sname := $fstruct.Serializer.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} -{{ end -}} -{{ $ptype := $fstruct.Type -}} - {{ if ne $ptype 1 -}} +{{ end }} func (obj *{{ $PublicStructName }}) Get{{ $fstruct.Name }}() {{ $rtype }} { return obj.field{{ $fstruct.Name }} } - {{ end }} {{ end }} type {{ $PublicStructName }}Params struct { -{{- range $ind, $fstruct := .ProcFieldList -}} +{{- range $ind, $fstruct := .ProcInFieldList -}} {{ $rtype := $fstruct.Format -}} - {{ $ptype := $fstruct.Type -}} {{ $serlen := len $fstruct.Serializer -}} {{ if ne $serlen 0 -}} {{ $sname := index $fstruct.Serializer 0 -}} {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end }} - {{- if ne $ptype 2 -}} {{ $fstruct.Name }} {{ $rtype -}} - {{- end }} {{ end }} } @@ -128,13 +120,16 @@ func (obj *{{ $PublicStructName }}) GetParams() {{ $PublicStructName }}Params { return obj.params } +func (obj *{{ $PublicStructName }}) setParams(params {{ $PublicStructName }}Params) error { + obj.params = params + + return nil +} + func (obj *{{ $PublicStructName }}Params) arrayValues() []string { return []string{ -{{- range $ind, $fstruct := .ProcFieldList -}} - {{ $ptype := $fstruct.Type -}} - {{- if ne $ptype 2 }} +{{- range $ind, $fstruct := .ProcInFieldList -}} obj.{{- $fstruct.Name -}}, - {{- end }} {{- end }} } } @@ -155,13 +150,17 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi return nil, err } - tuples, err := octopus.CallLua(ctx, connection, procName, params.arrayValues()...) + td, err := octopus.CallLua(ctx, connection, procName, params.arrayValues()...) if err != nil { metricErrCnt.Inc(ctx, "call_proc", 1) return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) } - ret, err := tupleToStruct(ctx, tuples) + if len(td) != 1 { + return nil, fmt.Errorf("invalid response len from lua call: %d. Only one tuple supported", len(td)) + } + + ret, err := TupleToStruct(ctx, td[0]) if err != nil { metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) logger.Error(ctx, "Error in response: ", err) @@ -174,25 +173,26 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi return ret, nil } -func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*{{ $PublicStructName }}, error) { +func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*{{ $PublicStructName }}, error) { + if tuple.Cnt < cntOutFields { + return nil, fmt.Errorf("not enought selected fields %d in response tuple: %d but expected %d fields", tuple.Cnt, tuple.Cnt, cntOutFields) + } + np := {{ $PublicStructName }}{} - {{ range $ind, $fstruct := .ProcFieldList -}} - {{ $ptype := $fstruct.Type -}} - {{ if ne $ptype 1 -}} - val{{ $fstruct.Name }}, err := Unpack{{ $fstruct.Name -}}(bytes.NewReader(tuples[{{$ind}} - 1].Data[0])) + {{ range $ind, $fstruct := .ProcOutFieldList -}} + val{{ $fstruct.Name }}, err := Unpack{{ $fstruct.Name -}}(bytes.NewReader(tuple.Data[{{$ind}}])) if err != nil { return nil, err } np.field{{ $fstruct.Name }} = val{{ $fstruct.Name }} - {{ end }} {{ end }} return &np, nil } -{{ range $ind, $fstruct := .ProcFieldList -}} +{{ range $ind, $fstruct := .ProcOutFieldList -}} {{ $packerparam := packerParam $fstruct.Format -}} {{ $rtype := $fstruct.Format -}} {{ $sname := $fstruct.Serializer.Name -}} @@ -200,14 +200,11 @@ func tupleToStruct(ctx context.Context, tuples []octopus.TupleData) (*{{ $Public {{ $serializer := index $serializers $sname -}} {{ $rtype = $serializer.Type -}} {{ end -}} - {{ $ptype := $fstruct.Type -}} - {{ if ne $ptype 1 -}} func (obj *{{ $PublicStructName }}) Set{{ $fstruct.Name }}({{ $fstruct.Name }} {{ $rtype }}) error { obj.field{{ $fstruct.Name }} = {{ $fstruct.Name}} return nil } - {{- end }} func Unpack{{ $fstruct.Name }}(r *bytes.Reader) (ret {{ $rtype }}, errRet error) { var {{ $fstruct.Name }} {{ if ne $packerparam.UnpackType "" }}{{ $packerparam.UnpackType }}{{ else }}{{ $fstruct.Format }}{{ end }} @@ -350,16 +347,15 @@ func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType return box, nil } -{{ if $fields }} func New(ctx context.Context) *{{ $PublicStructName }} { newObj := {{ $PublicStructName }}{} + {{- if $fields }} newObj.BaseField.UpdateOps = []octopus.Ops{} newObj.BaseField.ExtraFields = [][]byte{} newObj.BaseField.Objects = map[string][]octopus.ModelStruct{} - + {{ end }} return &newObj } -{{ end }} {{- if .Triggers.RepairTuple }} func repairTuple(ctx context.Context, tuple *octopus.TupleData) error { diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index c5dd8f4..84df54f 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -17,7 +17,7 @@ import ( {{ $pkgName := .ARPkg }} {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} -{{ $procfields := .ProcFieldList }} +{{ $procfields := .ProcOutFieldList }} {{ $serializers := .Serializers -}} {{ $fidx := .FieldMap }} @@ -29,16 +29,13 @@ func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { var err error -{{ range $ind, $fstruct := $procfields -}} -{{ $ptype := $fstruct.Type -}} - {{ if ne $ptype 1 -}} +{{ range $ind, $fstruct := .ProcOutFieldList -}} data, err = pack{{ $fstruct.Name }}([]byte{}, obj.Get{{ $fstruct.Name }}()) if err != nil { return nil, err } tuple = append(tuple, data) - {{- end }} {{ end }} return tuple, nil } diff --git a/internal/pkg/parser/import_w_test.go b/internal/pkg/parser/import_w_test.go index 39c5f84..2f4e201 100644 --- a/internal/pkg/parser/import_w_test.go +++ b/internal/pkg/parser/import_w_test.go @@ -42,7 +42,6 @@ func TestParseImport(t *testing.T) { PackageName: "", }, Backends: []string{}, - ProcFields: []ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, diff --git a/internal/pkg/parser/parser_b_test.go b/internal/pkg/parser/parser_b_test.go index 21f92dc..aedcc07 100644 --- a/internal/pkg/parser/parser_b_test.go +++ b/internal/pkg/parser/parser_b_test.go @@ -132,7 +132,6 @@ type TriggersFoo struct { }, }, FlagMap: map[string]ds.FlagDeclaration{}, - ProcFields: []ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, }, }, @@ -205,9 +204,12 @@ type ProcFieldsFoo struct { Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11111"}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, - ProcFields: []ds.ProcFieldDeclaration{ + ProcInFields: []ds.ProcFieldDeclaration{ {Name: "InParams1", Format: "string", Type: 1, Serializer: []string{}}, {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, + }, + ProcOutFields: []ds.ProcFieldDeclaration{ + {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, {Name: "Output", Format: "string", Type: 2, Serializer: []string{}}, }, ProcFieldsMap: map[string]int{"InOutParams2": 1, "InParams1": 0, "Output": 2}, diff --git a/internal/pkg/parser/parser_w_test.go b/internal/pkg/parser/parser_w_test.go index 9b6c88c..deb8f05 100644 --- a/internal/pkg/parser/parser_w_test.go +++ b/internal/pkg/parser/parser_w_test.go @@ -49,7 +49,6 @@ func Test_parseDoc(t *testing.T) { Backends: []string{"octopus"}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, - ProcFields: []ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, Indexes: []ds.IndexDeclaration{}, @@ -367,7 +366,6 @@ func Test_parseAst(t *testing.T) { want: &ds.RecordPackage{ Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, Namespace: ds.NamespaceDeclaration{Num: 5, PublicName: "Baz", PackageName: "baz"}, - ProcFields: []ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, From ea997bfc994dba4b1c22f13eba2e74f6dd1a0686 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:55:56 +0300 Subject: [PATCH 06/20] check field validation in procedure declaration --- internal/app/argen_w_test.go | 6 +- internal/pkg/arerror/checker.go | 4 + internal/pkg/checker/checker.go | 38 ++++- internal/pkg/checker/checker_b_test.go | 4 +- internal/pkg/checker/checker_w_test.go | 156 +++++++++++++++++- internal/pkg/ds/app.go | 20 ++- internal/pkg/generator/fixture_test.go | 6 +- internal/pkg/generator/generator.go | 2 - internal/pkg/generator/generator_b_test.go | 2 +- internal/pkg/generator/octopus.go | 1 + internal/pkg/generator/octopus_b_test.go | 13 +- internal/pkg/generator/tmpl/meta.tmpl | 17 +- .../generator/tmpl/octopus/fixturestore.tmpl | 4 + internal/pkg/generator/tmpl/octopus/main.tmpl | 4 +- internal/pkg/generator/tmpl/octopus/mock.tmpl | 18 ++ internal/pkg/parser/field.go | 6 +- internal/pkg/parser/import_w_test.go | 2 +- internal/pkg/parser/index.go | 71 -------- internal/pkg/parser/parser.go | 51 +++--- internal/pkg/parser/parser_b_test.go | 2 +- internal/pkg/parser/parser_w_test.go | 8 +- internal/pkg/parser/utils.go | 4 - 22 files changed, 290 insertions(+), 149 deletions(-) diff --git a/internal/app/argen_w_test.go b/internal/app/argen_w_test.go index 7b5c5cc..d6ed714 100644 --- a/internal/app/argen_w_test.go +++ b/internal/app/argen_w_test.go @@ -357,7 +357,7 @@ func TestArGen_preparePackage(t *testing.T) { rpFoo := ds.NewRecordPackage() rpFoo.Backends = []string{"octopus"} rpFoo.Server = ds.ServerDeclaration{Host: "127.0.0.1", Port: "11011"} - rpFoo.Namespace = ds.NamespaceDeclaration{Num: 0, PackageName: "foo", PublicName: "Foo"} + rpFoo.Namespace = ds.NamespaceDeclaration{ObjectName: "0", PackageName: "foo", PublicName: "Foo"} err := rpFoo.AddField(ds.FieldDeclaration{ Name: "ID", @@ -401,7 +401,7 @@ func TestArGen_preparePackage(t *testing.T) { rpBar := ds.NewRecordPackage() rpBar.Backends = []string{"octopus"} - rpBar.Namespace = ds.NamespaceDeclaration{Num: 1, PackageName: "bar", PublicName: "Bar"} + rpBar.Namespace = ds.NamespaceDeclaration{ObjectName: "1", PackageName: "bar", PublicName: "Bar"} err = rpBar.AddField(ds.FieldDeclaration{ Name: "ID", @@ -671,7 +671,7 @@ type TriggersFoo struct { wantErr: false, want: map[string]*ds.RecordPackage{ "foo": { - Namespace: ds.NamespaceDeclaration{Num: 2, PublicName: "Foo", PackageName: "foo"}, + Namespace: ds.NamespaceDeclaration{ObjectName: "2", PublicName: "Foo", PackageName: "foo"}, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11111"}, Fields: []ds.FieldDeclaration{ {Name: "Field1", Format: "int", PrimaryKey: true, Mutators: []ds.FieldMutator{}, Size: 5, Serializer: []string{}}, diff --git a/internal/pkg/arerror/checker.go b/internal/pkg/arerror/checker.go index 091f58a..390f18d 100644 --- a/internal/pkg/arerror/checker.go +++ b/internal/pkg/arerror/checker.go @@ -9,6 +9,7 @@ var ErrCheckBackendUnknown = errors.New("backend unknown") var ErrCheckEmptyNamespace = errors.New("empty namespace") var ErrCheckPkgBackendToMatch = errors.New("many backends for one class not supported yet") var ErrCheckFieldSerializerNotFound = errors.New("serializer not found") +var ErrCheckFieldSerializerNotSupported = errors.New("serializer not supported") var ErrCheckFieldInvalidFormat = errors.New("invalid format") var ErrCheckFieldMutatorConflictPK = errors.New("conflict mutators with primary_key") var ErrCheckFieldMutatorConflictSerializer = errors.New("conflict mutators with serializer") @@ -19,6 +20,9 @@ var ErrCheckPortEmpty = errors.New("serverPort is empty") var ErrCheckServerConflict = errors.New("conflict ServerHost and serverConf params") var ErrCheckFieldIndexEmpty = errors.New("field for index is empty") var ErrCheckObjectNotFound = errors.New("linked object not found") +var ErrCheckFieldTypeNotFound = errors.New("procedure field type not found") +var ErrCheckFieldsEmpty = errors.New("empty required field declaration") +var ErrCheckFieldsManyDecl = errors.New("few declarations of fields not supported") // Описание ошибки декларации пакета type ErrCheckPackageDecl struct { diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index f0bb78d..366e92e 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -66,6 +66,10 @@ func checkNamespace(ns *ds.NamespaceDeclaration) error { // - есть первичный ключ // - имена сущностей на которые ссылаемся на могут пересекаться с именами полей func checkFields(cl *ds.RecordPackage) error { + if len(cl.Fields) > 0 && len(cl.ProcOutFields) > 0 { + return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsManyDecl} + } + primaryFound := false octopusAvailFormat := map[octopus.Format]bool{} @@ -111,15 +115,41 @@ func checkFields(cl *ds.RecordPackage) error { } } + for _, fld := range cl.ProcInFields { + if _, ex := octopusAvailFormat[fld.Format]; !ex { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} + } + + if len(fld.Serializer) > 0 { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotSupported} + } + + if fld.Type == 0 { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldTypeNotFound} + } + } + for _, fld := range cl.ProcOutFields { - if fld.Type < 1 && fld.Type > 3 { + if _, ex := octopusAvailFormat[fld.Format]; !ex { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} + } + if len(fld.Serializer) > 0 { + if _, ex := cl.SerializerMap[fld.Serializer[0]]; !ex { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} + } + } + + if fld.Type == 0 { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldTypeNotFound} } - //TODO: добавить валидацию - primaryFound = true } - if !primaryFound { + if len(cl.Fields) == 0 && len(cl.ProcOutFields) == 0 { + return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsEmpty} + } + + if len(cl.Fields) > 0 && !primaryFound { return &arerror.ErrCheckPackageIndexDecl{Pkg: cl.Namespace.PackageName, Index: "primary", Err: arerror.ErrIndexNotExist} } diff --git a/internal/pkg/checker/checker_b_test.go b/internal/pkg/checker/checker_b_test.go index 147700d..d65afdf 100644 --- a/internal/pkg/checker/checker_b_test.go +++ b/internal/pkg/checker/checker_b_test.go @@ -11,7 +11,7 @@ import ( func TestCheck(t *testing.T) { rpFoo := ds.NewRecordPackage() rpFoo.Backends = []string{"octopus"} - rpFoo.Namespace = ds.NamespaceDeclaration{Num: 0, PackageName: "foo", PublicName: "Foo"} + rpFoo.Namespace = ds.NamespaceDeclaration{ObjectName: "0", PackageName: "foo", PublicName: "Foo"} rpFoo.Server = ds.ServerDeclaration{Host: "127.0.0.1", Port: "11011"} err := rpFoo.AddField(ds.FieldDeclaration{ @@ -56,7 +56,7 @@ func TestCheck(t *testing.T) { rpInvalidFormat := ds.NewRecordPackage() rpInvalidFormat.Backends = []string{"octopus"} - rpInvalidFormat.Namespace = ds.NamespaceDeclaration{Num: 0, PackageName: "invform", PublicName: "InvalidFormat"} + rpInvalidFormat.Namespace = ds.NamespaceDeclaration{ObjectName: "0", PackageName: "invform", PublicName: "InvalidFormat"} rpInvalidFormat.Server = ds.ServerDeclaration{Host: "127.0.0.1", Port: "11011"} err = rpInvalidFormat.AddField(ds.FieldDeclaration{ diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 913f432..52af039 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -85,7 +85,7 @@ func Test_checkNamespace(t *testing.T) { name: "normal namespace", args: args{ ns: &ds.NamespaceDeclaration{ - Num: 0, + ObjectName: "0", PublicName: "Foo", PackageName: "foo", }, @@ -96,7 +96,7 @@ func Test_checkNamespace(t *testing.T) { name: "empty name", args: args{ ns: &ds.NamespaceDeclaration{ - Num: 0, + ObjectName: "0", PublicName: "", PackageName: "foo", }, @@ -107,7 +107,7 @@ func Test_checkNamespace(t *testing.T) { name: "empty package", args: args{ ns: &ds.NamespaceDeclaration{ - Num: 0, + ObjectName: "0", PublicName: "Foo", PackageName: "", }, @@ -348,3 +348,153 @@ func Test_checkFields(t *testing.T) { }) } } + +func Test_checkProcFields(t *testing.T) { + type args struct { + cl ds.RecordPackage + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "empty fields", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{}, + }, + }, + wantErr: true, + }, + { + name: "2 fields declaration", + args: args{ + cl: ds.RecordPackage{ + Fields: []ds.FieldDeclaration{ + { + Name: "Foo", + Format: "int", + PrimaryKey: true, + }, + }, + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: ds.INTOUT, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "empty format", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "invalid format", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "[]int", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "type not found", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "normal field", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: ds.OUT, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "serializer not supported", + args: args{ + cl: ds.RecordPackage{ + ProcInFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: ds.IN, + Serializer: []string{ + "fser", + }, + }, + }, + SerializerMap: map[string]ds.SerializerDeclaration{ + "fser": {}, + }, + }, + }, + wantErr: true, + }, + { + name: "serializer not declared", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: 2, + }, + { + Name: "Foo", + Format: "int", + Type: 2, + Serializer: []string{ + "fser", + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := checkFields(&tt.args.cl); (err != nil) != tt.wantErr { + t.Errorf("checkFields() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 21409b3..86c554d 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -57,7 +57,6 @@ func (i *AppInfo) String() string { // Структура для описания неймспейса сущности type NamespaceDeclaration struct { ObjectName string - Num int64 PublicName string PackageName string ModuleName string @@ -181,13 +180,22 @@ func (s Serializer) Params() string { return "" } +type ProcParameterType uint8 + +const ( + _ ProcParameterType = iota + IN //тип входного параметра процедуры + OUT //тип выходного параметра процедуры + INTOUT //тип одновременно и входного и выходного параметра процедуры +) + // ProcFieldDeclaration Тип описывающий поле процедуры type ProcFieldDeclaration struct { - Name string // Название поля - Format octopus.Format // формат поля - Type uint8 // тип параметра (IN, OUT, INOUT) - Size int64 // Размер поля, используется только для строковых значений - Serializer Serializer // Сериализатора для поля + Name string // Название поля + Format octopus.Format // формат поля + Type ProcParameterType // тип параметра (IN, OUT, INOUT) + Size int64 // Размер поля, используется только для строковых значений + Serializer Serializer // Сериализатора для поля } // Тип и константы описывающие мутаторы для поля diff --git a/internal/pkg/generator/fixture_test.go b/internal/pkg/generator/fixture_test.go index b9f2be9..7a9a4db 100644 --- a/internal/pkg/generator/fixture_test.go +++ b/internal/pkg/generator/fixture_test.go @@ -13,8 +13,6 @@ func TestGenerateFixture(t *testing.T) { params PkgData } - namespaceNum := int64(2) - packageName := "gift" tests := []struct { @@ -84,7 +82,7 @@ func TestGenerateFixture(t *testing.T) { LinkedObject: map[string]ds.RecordPackage{ packageName: { Namespace: ds.NamespaceDeclaration{ - Num: 0, + ObjectName: "0", PublicName: "Gift", PackageName: "gift", ModuleName: "github.com/foo/bar/baz.git/internal/pkg/model/repository/cmpl/", @@ -92,7 +90,7 @@ func TestGenerateFixture(t *testing.T) { }, }, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, - Container: ds.NamespaceDeclaration{Num: namespaceNum, PublicName: "Testmodel", PackageName: "testmodel"}, + Container: ds.NamespaceDeclaration{ObjectName: "0", PublicName: "Testmodel", PackageName: "testmodel"}, Serializers: map[string]ds.SerializerDeclaration{}, Imports: []ds.ImportDeclaration{ { diff --git a/internal/pkg/generator/generator.go b/internal/pkg/generator/generator.go index 0ced0a6..76e6364 100644 --- a/internal/pkg/generator/generator.go +++ b/internal/pkg/generator/generator.go @@ -37,7 +37,6 @@ type PkgData struct { LinkedObject map[string]ds.RecordPackage ProcInFieldList []ds.ProcFieldDeclaration ProcOutFieldList []ds.ProcFieldDeclaration - ProcFieldMap map[string]int Server ds.ServerDeclaration Container ds.NamespaceDeclaration Indexes []ds.IndexDeclaration @@ -57,7 +56,6 @@ func NewPkgData(appInfo string, cl ds.RecordPackage) PkgData { FieldMap: cl.FieldsMap, ProcInFieldList: cl.ProcInFields, ProcOutFieldList: cl.ProcOutFields, - ProcFieldMap: cl.ProcFieldsMap, FieldObject: cl.FieldsObjectMap, Server: cl.Server, Container: cl.Namespace, diff --git a/internal/pkg/generator/generator_b_test.go b/internal/pkg/generator/generator_b_test.go index 07908bb..5617335 100644 --- a/internal/pkg/generator/generator_b_test.go +++ b/internal/pkg/generator/generator_b_test.go @@ -31,7 +31,7 @@ func TestGenerate(t *testing.T) { Timeout: 500, }, Namespace: ds.NamespaceDeclaration{ - Num: 5, + ObjectName: "5", PublicName: "Bar", PackageName: "bar", }, diff --git a/internal/pkg/generator/octopus.go b/internal/pkg/generator/octopus.go index 9e527a0..c8a5ebd 100644 --- a/internal/pkg/generator/octopus.go +++ b/internal/pkg/generator/octopus.go @@ -37,6 +37,7 @@ func GenerateOctopus(params PkgData) (map[string]bytes.Buffer, *arerror.ErrGener octopusFile := bufio.NewWriter(&octopusWriter) + //TODO возможно имеет смысл разделить большой шаблон OctopusRootRepositoryTmpl для удобства поддержки err := GenerateByTmpl(octopusFile, params, "octopus", OctopusRootRepositoryTmpl) if err != nil { return nil, err diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index a8f7ea2..91d4c45 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -1,7 +1,6 @@ package generator import ( - "strconv" "strings" "testing" @@ -14,8 +13,7 @@ func TestGenerateOctopus(t *testing.T) { params PkgData } - namespaceNum := int64(2) - namespaceStr := strconv.FormatUint(uint64(namespaceNum), 10) + namespaceStr := "2" packageName := "foo" @@ -76,7 +74,7 @@ func TestGenerateOctopus(t *testing.T) { }, FieldObject: map[string]ds.FieldObject{}, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, - Container: ds.NamespaceDeclaration{Num: namespaceNum, PublicName: "Testmodel", PackageName: "testmodel"}, + Container: ds.NamespaceDeclaration{ObjectName: "2", PublicName: "Testmodel", PackageName: "testmodel"}, Serializers: map[string]ds.SerializerDeclaration{}, Imports: []ds.ImportDeclaration{}, Triggers: map[string]ds.TriggerDeclaration{}, @@ -158,10 +156,9 @@ func TestGenerateOctopus(t *testing.T) { Serializer: []string{"s2i"}, }, }, - ProcFieldMap: map[string]int{}, - Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, - Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, - Indexes: []ds.IndexDeclaration{{}}, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, + Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, + Indexes: []ds.IndexDeclaration{{}}, Serializers: map[string]ds.SerializerDeclaration{ "s2i": { Name: "Output", diff --git a/internal/pkg/generator/tmpl/meta.tmpl b/internal/pkg/generator/tmpl/meta.tmpl index 95b3ed6..ec3e5e4 100644 --- a/internal/pkg/generator/tmpl/meta.tmpl +++ b/internal/pkg/generator/tmpl/meta.tmpl @@ -23,12 +23,17 @@ type FieldMeta struct { Unpacker func(packedField []byte) (any, error) } -type NSPackage map[uint32]SpaceMeta +type NSPackage map[string]SpaceMeta + +func (ns NSPackage) meta(n uint32) (SpaceMeta, bool) { + v, ok := ns[strconv.Itoa(int(n))] + return v, ok +} {{ $nss := .Namespaces }} var NamespacePackages = NSPackage { {{ range $_, $ns := $nss -}} - {{ $ns.Namespace.Num }}: { + "{{ $ns.Namespace.ObjectName }}": { PackageName: "{{ $ns.Namespace.PackageName }}", Unpacker: func(ctx context.Context, tuple octopus.TupleData) (any, error) { obj, err := {{ $ns.Namespace.PackageName }}.TupleToStruct(ctx, tuple) @@ -70,7 +75,7 @@ var NamespacePackages = NSPackage { } func (n NSPackage) GetSelectDebugInfo(ns uint32, indexnum uint32, offset uint32, limit uint32, keys [][][]byte) string { - spacemeta, ex := n[ns] + spacemeta, ex := n.meta(ns) if !ex { return fmt.Sprintf("unknown space %d, index: %d, offset: %d, limit: %d, Keys: %+v", ns, indexnum, offset, limit, keys) } @@ -89,7 +94,7 @@ func (n NSPackage) GetSelectDebugInfo(ns uint32, indexnum uint32, offset uint32, } func (n NSPackage) GetUpdateDebugInfo(ns uint32, primaryKey [][]byte, updateOps []octopus.Ops) string { - spacemeta, ex := n[ns] + spacemeta, ex := n.meta(ns) if !ex { return fmt.Sprintf("unknown space %d, primaryKey: %+v, updateOps: %+v", ns, primaryKey, updateOps) } @@ -114,7 +119,7 @@ func (n NSPackage) GetUpdateDebugInfo(ns uint32, primaryKey [][]byte, updateOps } func (n NSPackage) GetDeleteDebugInfo(ns uint32, primaryKey [][]byte) string { - spacemeta, ex := n[ns] + spacemeta, ex := n.meta(ns) if !ex { return fmt.Sprintf("unknown space %d, primaryKey: %+v", ns, primaryKey) } @@ -130,7 +135,7 @@ func (n NSPackage) GetDeleteDebugInfo(ns uint32, primaryKey [][]byte) string { func (n NSPackage) GetInsertDebugInfo(ns uint32, needRetVal bool, insertMode octopus.InsertMode, tuple octopus.TupleData) string { strMode := octopus.GetInsertModeName(insertMode) - spacemeta, ex := n[ns] + spacemeta, ex := n.meta(ns) if !ex { return fmt.Sprintf("unknown space %d, insertMode: %s, tuple: %+v", ns, strMode, tuple) } diff --git a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl index ebffabb..b681310 100644 --- a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl @@ -65,6 +65,10 @@ func Get{{$PublicStructName}}ByParams(params {{$PackageName}}.{{$PublicStructNam type {{ $PublicStructName }}ProcedureMocker struct {} +func Get{{ $PublicStructName }}ProcedureMocker() {{ $PublicStructName }}ProcedureMocker { + return {{ $PublicStructName }}ProcedureMocker{} +} + func (m {{ $PublicStructName }}ProcedureMocker) ByFixtureParams(ctx context.Context, params {{$PackageName}}.FooParams) octopus.FixtureType { return m.ByParamsMocks(ctx, params, []octopus.MockEntities{ diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index cc8cd17..c407eb7 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -46,7 +46,7 @@ import ( type {{ $PublicStructName }}List []*{{ $PublicStructName }} const ( - namespace uint32 = {{ .Container.Num }} + namespace uint32 = {{ .Container.ObjectName }} cntFields uint32 = {{ len .FieldList }} {{- range $fieldname, $flag := .Flags -}} {{ range $i, $flagname := $flag.Flags }} @@ -170,6 +170,8 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi metricTimer.Finish(ctx, "call_proc") + activerecord.Logger().CollectQueries(ctx, CallMockerLogger(params, {{ $PublicStructName }}List([]*{{ $PublicStructName }}{ret}))) + return ret, nil } diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index 84df54f..37d0b3e 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -59,6 +59,24 @@ func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, erro return data, err } + +func CallMockerLogger(params {{ $PublicStructName }}Params, res {{ $PublicStructName }}List) func() (activerecord.MockerLogger, error) { +return func() (activerecord.MockerLogger, error){ + + mockerName := "mocker{{ $PublicStructName }}ByParams" + mocker := "fixture.Get{{ $PublicStructName }}ProcedureMocker()" + + fixture := "ps := {{$PublicStructName}}Params{ \n" + {{- range $ind, $fstruct := .ProcInFieldList }} + fixture += "{{$fstruct.Name}}: params.{{$fstruct.Name}},\n" + {{- end }} + fixture += "}\n" + fixture += mocker + fixture += ".ByFixtureParams(ctx, ps)" + + return activerecord.MockerLogger{MockerName: mockerName, Mockers: mocker, FixturesSelector: fixture, ResultName: "{{ $pkgName }}", Results: res}, nil + } +} {{- end }} {{ if $fields }} diff --git a/internal/pkg/parser/field.go b/internal/pkg/parser/field.go index c99ca43..0ce953e 100644 --- a/internal/pkg/parser/field.go +++ b/internal/pkg/parser/field.go @@ -129,9 +129,11 @@ func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) err for _, kv := range tagParam { switch TagNameType(kv[0]) { case ProcInputParamTag: - newfield.Type = newfield.Type | 1 + //результат бинарной операции 0|IN => IN; 1|IN => IN; 2|IN => INTOUT (3); + newfield.Type = newfield.Type | ds.IN case ProcOutputParamTag: - newfield.Type = newfield.Type | 2 + //результат бинарной операции 0|OUT => OUT; 1|OUT => INTOUT (3); 2|OUT => OUT; + newfield.Type = newfield.Type | ds.OUT case SizeTag: if kv[1] != "" { size, err := strconv.ParseInt(kv[1], 10, 64) diff --git a/internal/pkg/parser/import_w_test.go b/internal/pkg/parser/import_w_test.go index 2f4e201..8a3a5e4 100644 --- a/internal/pkg/parser/import_w_test.go +++ b/internal/pkg/parser/import_w_test.go @@ -37,7 +37,7 @@ func TestParseImport(t *testing.T) { Timeout: 0, }, Namespace: ds.NamespaceDeclaration{ - Num: 0, + ObjectName: "", PublicName: "", PackageName: "", }, diff --git a/internal/pkg/parser/index.go b/internal/pkg/parser/index.go index 42759b8..f270211 100644 --- a/internal/pkg/parser/index.go +++ b/internal/pkg/parser/index.go @@ -172,74 +172,3 @@ func ParseIndexes(dst *ds.RecordPackage, fields []*ast.Field) error { return nil } - -func ParseParams(dst *ds.RecordPackage, fields []*ast.Field) error { - if len(fields) == 0 { - return nil - } - - for _, field := range fields { - if field.Names == nil || len(field.Names) != 1 { - return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Err: arerror.ErrNameDeclaration} - } - - ind := ds.IndexDeclaration{ - Name: field.Names[0].Name, - Fields: []int{}, - FieldsMap: map[string]ds.IndexField{}, - Selector: "SelectBy" + field.Names[0].Name} - - if err := checkBoolType(field.Type); err != nil { - return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Name: ind.Name, Err: arerror.ErrTypeNotBool} - } - - if err := ParseParamTag(field, &ind, dst.FieldsMap); err != nil { - return fmt.Errorf("error parse indexTag: %w", err) - } - - if err := dst.AddIndex(ind); err != nil { - return err - } - } - - return nil -} - -func ParseParamTag(field *ast.Field, ind *ds.IndexDeclaration, fieldsMap map[string]int) error { - tagParam, err := splitTag(field, CheckFlagEmpty, map[TagNameType]ParamValueRule{PrimaryKeyTag: ParamNotNeedValue, UniqueTag: ParamNotNeedValue}) - if err != nil { - return &arerror.ErrParseTypeIndexDecl{IndexType: "index", Name: ind.Name, Err: err} - } - - for _, kv := range tagParam { - switch TagNameType(kv[0]) { - case SelectorTag: - ind.Selector = kv[1] - case FieldsTag: - for _, fieldName := range strings.Split(kv[1], ",") { - if fldNum, ex := fieldsMap[fieldName]; !ex { - return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} - } else if _, ex := ind.FieldsMap[fieldName]; ex { - return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrDuplicate} - } else { - ind.FieldsMap[fieldName] = ds.IndexField{IndField: fldNum, Order: ds.IndexOrderAsc} - ind.Fields = append(ind.Fields, fldNum) - } - } - case OrderDescTag: - for _, fn := range strings.Split(kv[1], ",") { - if _, ex := fieldsMap[fn]; !ex { - return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} - } else if indField, ex := ind.FieldsMap[fn]; !ex { - return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrFieldNotExist} - } else { - indField.Order = ds.IndexOrderDesc - } - } - default: - return &arerror.ErrParseTypeIndexTagDecl{IndexType: "index", Name: ind.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrParseTagUnknown} - } - } - - return nil -} diff --git a/internal/pkg/parser/parser.go b/internal/pkg/parser/parser.go index 27e57a9..b8bc9ab 100644 --- a/internal/pkg/parser/parser.go +++ b/internal/pkg/parser/parser.go @@ -88,6 +88,29 @@ func parseAst(pkgName string, decls []ast.Decl, rc *ds.RecordPackage) error { return nil } +func parseStructNameType(dst *ds.RecordPackage, nodeName string, curr *ast.StructType) error { + switch StructNameType(nodeName) { + case Fields: + return ParseFields(dst, curr.Fields.List) + case FieldsObject: + return ParseFieldsObject(dst, curr.Fields.List) + case Indexes: + return ParseIndexes(dst, curr.Fields.List) + case IndexParts: + return ParseIndexPart(dst, curr.Fields.List) + case Serializers: + return ParseSerializer(dst, curr.Fields.List) + case Triggers: + return ParseTrigger(dst, curr.Fields.List) + case Flags: + return ParseFlags(dst, curr.Fields.List) + case ProcFields: + return ParseProcFields(dst, curr.Fields.List) + default: + return arerror.ErrUnknown + } +} + func parseStructType(dst *ds.RecordPackage, name string, doc *ast.CommentGroup, curr *ast.StructType) error { nodeName, public, private, err := getNodeName(name) if err != nil { @@ -141,29 +164,6 @@ func parseTokenType(dst *ds.RecordPackage, genD *ast.GenDecl) error { return nil } -func parseStructNameType(dst *ds.RecordPackage, nodeName string, curr *ast.StructType) error { - switch StructNameType(nodeName) { - case Fields: - return ParseFields(dst, curr.Fields.List) - case FieldsObject: - return ParseFieldsObject(dst, curr.Fields.List) - case Indexes: - return ParseIndexes(dst, curr.Fields.List) - case IndexParts: - return ParseIndexPart(dst, curr.Fields.List) - case Serializers: - return ParseSerializer(dst, curr.Fields.List) - case Triggers: - return ParseTrigger(dst, curr.Fields.List) - case Flags: - return ParseFlags(dst, curr.Fields.List) - case ProcFields: - return ParseProcFields(dst, curr.Fields.List) - default: - return arerror.ErrUnknown - } -} - // parseTokenImport финальные проверки перед парсингом импорта func parseTokenImport(dst *ds.RecordPackage, genD *ast.GenDecl) error { for _, spec := range genD.Specs { @@ -225,16 +225,15 @@ func parseDoc(dst *ds.RecordPackage, nodeName string, doc *ast.CommentGroup) err } dst.Server.Timeout = timeout - // TODO: привести к общему строковому типу независимому от бекэнда и типа объекта case "namespace": switch StructNameType(nodeName) { case Fields: - nsnum, err := strconv.ParseInt(kv[1], 10, 64) + _, err := strconv.ParseInt(kv[1], 10, 64) if err != nil { return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} } - dst.Namespace.Num = nsnum + dst.Namespace.ObjectName = kv[1] case ProcFields: if len(kv[1]) == 0 { return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} diff --git a/internal/pkg/parser/parser_b_test.go b/internal/pkg/parser/parser_b_test.go index aedcc07..71a23d9 100644 --- a/internal/pkg/parser/parser_b_test.go +++ b/internal/pkg/parser/parser_b_test.go @@ -76,7 +76,7 @@ type TriggersFoo struct { }, wantErr: false, want: &ds.RecordPackage{ - Namespace: ds.NamespaceDeclaration{Num: 2, PublicName: "Foo", PackageName: "foo"}, + Namespace: ds.NamespaceDeclaration{ObjectName: "2", PublicName: "Foo", PackageName: "foo"}, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11111"}, Fields: []ds.FieldDeclaration{ {Name: "Field1", Format: "int", PrimaryKey: true, Mutators: []ds.FieldMutator{}, Size: 5, Serializer: []string{}}, diff --git a/internal/pkg/parser/parser_w_test.go b/internal/pkg/parser/parser_w_test.go index deb8f05..7da92f2 100644 --- a/internal/pkg/parser/parser_w_test.go +++ b/internal/pkg/parser/parser_w_test.go @@ -42,7 +42,7 @@ func Test_parseDoc(t *testing.T) { Timeout: 500, }, Namespace: ds.NamespaceDeclaration{ - Num: 5, + ObjectName: "5", PublicName: "", PackageName: "", }, @@ -112,7 +112,7 @@ func Test_parseGen(t *testing.T) { w := ds.NewRecordPackage() w.Backends = []string{"octopus"} w.Namespace = ds.NamespaceDeclaration{ - Num: 5, + ObjectName: "5", PublicName: "Baz", PackageName: "baz", } @@ -124,7 +124,7 @@ func Test_parseGen(t *testing.T) { wLinked := ds.NewRecordPackage() wLinked.Backends = []string{"octopus"} wLinked.Namespace = ds.NamespaceDeclaration{ - Num: 5, + ObjectName: "5", PublicName: "Foo", PackageName: "foo", } @@ -365,7 +365,7 @@ func Test_parseAst(t *testing.T) { wantErr: false, want: &ds.RecordPackage{ Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, - Namespace: ds.NamespaceDeclaration{Num: 5, PublicName: "Baz", PackageName: "baz"}, + Namespace: ds.NamespaceDeclaration{ObjectName: "5", PublicName: "Baz", PackageName: "baz"}, ProcFieldsMap: map[string]int{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, diff --git a/internal/pkg/parser/utils.go b/internal/pkg/parser/utils.go index df53c26..1ced297 100644 --- a/internal/pkg/parser/utils.go +++ b/internal/pkg/parser/utils.go @@ -94,10 +94,6 @@ func splitParam(str string, rule map[TagNameType]ParamValueRule) ([][]string, er r = rule[NameDefaultRule] } - //if r == ParamNeedValue && len(kv) != 2 { - // return nil, &arerror.ErrParseTagDecl{Name: kv[0], Err: arerror.ErrParseTagNoValue} - //} - if r == ParamNotNeedValue && len(kv) == 2 { return nil, &arerror.ErrParseTagDecl{Name: kv[0], Err: arerror.ErrParseTagWithValue} } From 1454a8704650d7602746d87d2cb15e66640332a1 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:51:02 +0300 Subject: [PATCH 07/20] fix linter warning --- .golangci.yml | 2 +- internal/pkg/checker/checker_w_test.go | 2 +- internal/pkg/ds/app.go | 15 ++++----------- internal/pkg/ds/package.go | 4 ++-- internal/pkg/generator/octopus_b_test.go | 10 +++++----- internal/pkg/generator/tmpl/octopus/mock.tmpl | 4 ++-- internal/pkg/parser/field.go | 8 ++++---- 7 files changed, 19 insertions(+), 26 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index b8f24bf..bb2c306 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,7 +41,7 @@ linters: - staticcheck # enable before push - gocyclo # - dupl # - it's very slow, enable if you really know why you need it - - gocognit +# - gocognit - prealloc - gochecknoinits # - wsl diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 52af039..15f164e 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -382,7 +382,7 @@ func Test_checkProcFields(t *testing.T) { { Name: "Foo", Format: "int", - Type: ds.INTOUT, + Type: ds.INOUT, }, }, }, diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 86c554d..150fa71 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -127,13 +127,6 @@ type IndexField struct { Order IndexOrder } -// Тип для описания входных параметров при вызове процедуры -type ParametersDeclaration struct { - Name string // Имя параметра - Selector string // Название функции селектора - Fields []int // Список номеров полей -} - // Тип для описания индекса type IndexDeclaration struct { Name string // Имя индекса @@ -183,10 +176,10 @@ func (s Serializer) Params() string { type ProcParameterType uint8 const ( - _ ProcParameterType = iota - IN //тип входного параметра процедуры - OUT //тип выходного параметра процедуры - INTOUT //тип одновременно и входного и выходного параметра процедуры + _ ProcParameterType = iota + IN //тип входного параметра процедуры + OUT //тип выходного параметра процедуры + INOUT //тип одновременно и входного и выходного параметра процедуры ) // ProcFieldDeclaration Тип описывающий поле процедуры diff --git a/internal/pkg/ds/package.go b/internal/pkg/ds/package.go index 5dd5dea..bb2efba 100644 --- a/internal/pkg/ds/package.go +++ b/internal/pkg/ds/package.go @@ -44,11 +44,11 @@ func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { // добавляем поле и не забываем про обратны индекс rc.ProcFieldsMap[f.Name] = len(rc.ProcFieldsMap) // добавляем поле во входные параметры - if f.Type == 1 || f.Type == 3 { + if f.Type == IN || f.Type == INOUT { rc.ProcInFields = append(rc.ProcInFields, f) } // добавляем поле в выходные параметры - if f.Type == 2 || f.Type == 3 { + if f.Type == OUT || f.Type == INOUT { rc.ProcOutFields = append(rc.ProcOutFields, f) } diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 91d4c45..777cc94 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -120,7 +120,7 @@ func TestGenerateOctopus(t *testing.T) { }, }, { - name: "simpleProcPkg", + name: "procPkg", want: nil, args: args{ params: PkgData{ @@ -132,13 +132,13 @@ func TestGenerateOctopus(t *testing.T) { { Name: "Input", Format: "string", - Type: 1, + Type: ds.IN, Serializer: []string{}, }, { Name: "InputOutput", Format: "string", - Type: 2, + Type: ds.OUT, Serializer: []string{}, }, }, @@ -146,13 +146,13 @@ func TestGenerateOctopus(t *testing.T) { { Name: "InputOutput", Format: "string", - Type: 2, + Type: ds.OUT, Serializer: []string{}, }, { Name: "Output", Format: "string", - Type: 3, + Type: ds.INOUT, Serializer: []string{"s2i"}, }, }, diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index 37d0b3e..ed8ddd0 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -98,7 +98,7 @@ func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { {{ end }} return tuple, nil } -{{ if $fields }} //indexes +{{ if $fields }} {{ $pktype := "" }} {{ $pklenfld := 1 }} {{ $pkind := index .Indexes 0 }} @@ -307,7 +307,7 @@ return func() (activerecord.MockerLogger, error){ } {{ end }} -{{ end }} //indexes +{{ end }} func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, error) { data, err := SelectByPrimary(ctx, obj.Primary()) if err != nil { diff --git a/internal/pkg/parser/field.go b/internal/pkg/parser/field.go index 0ce953e..2a6c98c 100644 --- a/internal/pkg/parser/field.go +++ b/internal/pkg/parser/field.go @@ -118,7 +118,7 @@ func ParseFields(dst *ds.RecordPackage, fields []*ast.Field) error { return nil } -// Функция парсинга тегов полей модели +// ParseProcFieldsTag парсинг тегов полей декларации процедуры func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) error { tagParam, err := splitTag(field, NoCheckFlag, map[TagNameType]ParamValueRule{PrimaryKeyTag: ParamNotNeedValue, UniqueTag: ParamNotNeedValue}) if err != nil { @@ -129,10 +129,10 @@ func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) err for _, kv := range tagParam { switch TagNameType(kv[0]) { case ProcInputParamTag: - //результат бинарной операции 0|IN => IN; 1|IN => IN; 2|IN => INTOUT (3); + //результат бинарной операции 0|IN => IN; 1|IN => IN; 2|IN => INOUT (3); newfield.Type = newfield.Type | ds.IN case ProcOutputParamTag: - //результат бинарной операции 0|OUT => OUT; 1|OUT => INTOUT (3); 2|OUT => OUT; + //результат бинарной операции 0|OUT => OUT; 1|OUT => INOUT (3); 2|OUT => OUT; newfield.Type = newfield.Type | ds.OUT case SizeTag: if kv[1] != "" { @@ -154,7 +154,7 @@ func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) err return nil } -// Функция парсинга полей процедуры +// ParseProcFields парсинг полей процедуры func ParseProcFields(dst *ds.RecordPackage, fields []*ast.Field) error { for _, field := range fields { if field.Names == nil || len(field.Names) != 1 { From de94205bc2106a59e3787db942a87ab74d77500f Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Wed, 26 Apr 2023 05:19:39 +0300 Subject: [PATCH 08/20] refactoring templates --- internal/pkg/generator/fixture_test.go | 108 ++++++++++++++++++ internal/pkg/generator/octopus_b_test.go | 73 +++++++++++- .../pkg/generator/tmpl/octopus/fixture.tmpl | 3 +- .../generator/tmpl/octopus/fixturestore.tmpl | 14 ++- internal/pkg/generator/tmpl/octopus/main.tmpl | 92 ++------------- internal/pkg/generator/tmpl/octopus/mock.tmpl | 16 ++- pkg/octopus/box.go | 73 ++++++++++++ 7 files changed, 285 insertions(+), 94 deletions(-) diff --git a/internal/pkg/generator/fixture_test.go b/internal/pkg/generator/fixture_test.go index 7a9a4db..933691a 100644 --- a/internal/pkg/generator/fixture_test.go +++ b/internal/pkg/generator/fixture_test.go @@ -109,6 +109,114 @@ func TestGenerateFixture(t *testing.T) { }, }, }, + { + name: "simpleProcPkg", + want: nil, + args: args{ + params: PkgData{ + ARPkg: packageName, + ARPkgTitle: "Gift", + FieldList: []ds.FieldDeclaration{}, + FieldMap: map[string]int{}, + ProcInFieldList: []ds.ProcFieldDeclaration{}, + ProcOutFieldList: []ds.ProcFieldDeclaration{ + { + Name: "Output", + Format: "string", + Type: ds.OUT, + Serializer: []string{}, + }, + }, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, + Container: ds.NamespaceDeclaration{ObjectName: "simpleProc", PublicName: "Testmodel", PackageName: "testmodel"}, + Indexes: []ds.IndexDeclaration{}, + Serializers: map[string]ds.SerializerDeclaration{}, + Imports: []ds.ImportDeclaration{}, + Triggers: map[string]ds.TriggerDeclaration{}, + Flags: map[string]ds.FlagDeclaration{}, + AppInfo: "", + }, + }, + wantStr: map[string][]string{ + "fixture": { + `var giftStore map[gift.GiftParams]*gift.Gift`, + `func initGift() {`, + `func GetGiftByParams(params gift.GiftParams) *gift.Gift {`, + `type GiftProcedureMocker struct {}`, + `func GetGiftProcedureMocker() GiftProcedureMocker {`, + `func (m GiftProcedureMocker) ByFixture(ctx context.Context) octopus.FixtureType {`, + `func (m GiftProcedureMocker) ByMocks(ctx context.Context, mocks []octopus.MockEntities) octopus.FixtureType {`, + }, + }, + }, + { + name: "procPkg", + want: nil, + args: args{ + params: PkgData{ + ARPkg: packageName, + ARPkgTitle: "Gift", + FieldList: []ds.FieldDeclaration{}, + FieldMap: map[string]int{}, + ProcInFieldList: []ds.ProcFieldDeclaration{ + { + Name: "Input", + Format: "string", + Type: ds.IN, + Serializer: []string{}, + }, + { + Name: "InputOutput", + Format: "string", + Type: ds.OUT, + Serializer: []string{}, + }, + }, + ProcOutFieldList: []ds.ProcFieldDeclaration{ + { + Name: "InputOutput", + Format: "string", + Type: ds.OUT, + Serializer: []string{}, + }, + { + Name: "Output", + Format: "string", + Type: ds.INOUT, + Serializer: []string{"s2i"}, + }, + }, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, + Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, + Indexes: []ds.IndexDeclaration{}, + Serializers: map[string]ds.SerializerDeclaration{ + "s2i": { + Name: "Output", + Pkg: "github.com/mailru/activerecord/pkg/serializer", + Type: "int", + ImportName: "serializerOutput", + Marshaler: "OutputMarshal", + Unmarshaler: "OutputUnmarshal", + }, + }, + Imports: []ds.ImportDeclaration{}, + Triggers: map[string]ds.TriggerDeclaration{}, + Flags: map[string]ds.FlagDeclaration{}, + AppInfo: "", + }, + }, + wantStr: map[string][]string{ + "fixture": { + `var giftStore map[gift.GiftParams]*gift.Gift`, + `func initGift() {`, + `func GetGiftByParams(params gift.GiftParams) *gift.Gift {`, + `type GiftProcedureMocker struct {}`, + `func GetGiftProcedureMocker() GiftProcedureMocker {`, + `func (m GiftProcedureMocker) ByFixtureParams(ctx context.Context, params gift.GiftParams) octopus.FixtureType {`, + `func (m GiftProcedureMocker) ByParamsMocks(ctx context.Context, params gift.GiftParams, mocks []octopus.MockEntities) octopus.FixtureType {`, + }, + }, + }, } for _, tt := range tests { diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 777cc94..5a33dfb 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -101,7 +101,6 @@ func TestGenerateOctopus(t *testing.T) { `func NewFromBox(ctx context.Context, tuples []octopus.TupleData) ([]*Foo, error) {`, `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, `func New(ctx context.Context) *Foo {`, - `func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) {`, `namespace uint32 = ` + namespaceStr, `type Foo struct {`, `package ` + packageName, @@ -117,6 +116,66 @@ func TestGenerateOctopus(t *testing.T) { `func (obj *Foo) MockSelectByField1sRequest(ctx context.Context, keys [], ) []byte {`, `func (obj *Foo) MockSelectResponse() ([][]byte, error) {`, }, + "fixture": { + `type FooFT struct {`, + `func MarshalFixtures(objs []*Foo) ([]byte, error) {`, + `func UnmarshalFixtures(source []byte) []*Foo {`, + `func (objs FooList) String() string {`, + }, + }, + }, + { + name: "simpleProcPkg", + want: nil, + args: args{ + params: PkgData{ + ARPkg: packageName, + ARPkgTitle: "Foo", + FieldList: []ds.FieldDeclaration{}, + FieldMap: map[string]int{}, + ProcInFieldList: []ds.ProcFieldDeclaration{}, + ProcOutFieldList: []ds.ProcFieldDeclaration{ + { + Name: "Output", + Format: "string", + Type: ds.OUT, + Serializer: []string{}, + }, + }, + Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, + Container: ds.NamespaceDeclaration{ObjectName: "simpleProc", PublicName: "Testmodel", PackageName: "testmodel"}, + Indexes: []ds.IndexDeclaration{}, + Serializers: map[string]ds.SerializerDeclaration{}, + Imports: []ds.ImportDeclaration{}, + Triggers: map[string]ds.TriggerDeclaration{}, + Flags: map[string]ds.FlagDeclaration{}, + AppInfo: "", + }, + }, + wantStr: map[string][]string{ + "octopus": { + `Code generated by argen. DO NOT EDIT.`, + `func (obj *Foo) GetOutput() string {`, + `func Call(ctx context.Context) (*Foo, error)`, + `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, + `procName string = "simpleProc"`, + `type Foo struct {`, + `type FooParams struct {`, + `package ` + packageName, + }, + "mock": { + `func (obj *Foo) RepoSelector(ctx context.Context) (any, error) {`, + `func CallMockerLogger(res FooList) func() (activerecord.MockerLogger, error) {`, + `func MockCallRequest(ctx context.Context) []byte {`, + `func (obj *Foo) MockSelectResponse() ([][]byte, error) {`, + }, + "fixture": { + `type FooFTPK struct {`, + `type FooFT struct {`, + `func MarshalFixtures(objs []*Foo) ([]byte, error) {`, + `func UnmarshalFixtures(source []byte) []*Foo {`, + `func (objs FooList) String() string {`, + }, }, }, { @@ -158,7 +217,7 @@ func TestGenerateOctopus(t *testing.T) { }, Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, Container: ds.NamespaceDeclaration{ObjectName: "bar", PublicName: "Testmodel", PackageName: "testmodel"}, - Indexes: []ds.IndexDeclaration{{}}, + Indexes: []ds.IndexDeclaration{}, Serializers: map[string]ds.SerializerDeclaration{ "s2i": { Name: "Output", @@ -182,7 +241,6 @@ func TestGenerateOctopus(t *testing.T) { `func (obj *Foo) GetInputOutput() string {`, `func Call(ctx context.Context, params FooParams) (*Foo, error)`, `func TupleToStruct(ctx context.Context, tuple octopus.TupleData) (*Foo, error) {`, - `func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) {`, `procName string = "bar"`, `type Foo struct {`, `type FooParams struct {`, @@ -191,10 +249,17 @@ func TestGenerateOctopus(t *testing.T) { }, "mock": { `func (obj *Foo) RepoSelector(ctx context.Context) (any, error) {`, - //`func SelectByField1MockerLogger(keys [], res FooList) func() (activerecord.MockerLogger, error) {`, + `func CallMockerLogger(params FooParams, res FooList) func() (activerecord.MockerLogger, error) {`, `func MockCallRequest(ctx context.Context, params FooParams) []byte {`, `func (obj *Foo) MockSelectResponse() ([][]byte, error) {`, }, + "fixture": { + `type FooFTPK struct {`, + `type FooFT struct {`, + `func MarshalFixtures(objs []*Foo) ([]byte, error) {`, + `func UnmarshalFixtures(source []byte) []*Foo {`, + `func (objs FooList) String() string {`, + }, }, }, } diff --git a/internal/pkg/generator/tmpl/octopus/fixture.tmpl b/internal/pkg/generator/tmpl/octopus/fixture.tmpl index 0de479e..cbf0f7d 100644 --- a/internal/pkg/generator/tmpl/octopus/fixture.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixture.tmpl @@ -28,6 +28,7 @@ import ( {{ $flags := .Flags }} {{ $fields := .FieldList }} {{ $procfields := .ProcOutFieldList }} +{{ $procInLen := len .ProcInFieldList }} {{ $typePK := "" -}} {{ $fieldNamePK := "" -}} @@ -56,7 +57,7 @@ type {{ $PublicStructName }}FT struct { func MarshalFixtures(objs []*{{$PublicStructName}}) ([]byte, error) { fts := make([]{{$PublicStructName}}FT, 0, len(objs)) for _, obj := range objs { - params := obj.GetParams() + {{ if ne $procInLen 0 }}params := obj.GetParams(){{ end }} pk := {{$PublicStructName}}FTPK{ {{- range $ind, $fstruct := .ProcInFieldList }} diff --git a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl index b681310..40f3680 100644 --- a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl @@ -24,6 +24,7 @@ import ( {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} {{ $procfields := .ProcOutFieldList }} +{{ $procInLen := len .ProcInFieldList }} {{ $typePK := "" -}} {{ $fieldNamePK := "" -}} @@ -69,17 +70,24 @@ func Get{{ $PublicStructName }}ProcedureMocker() {{ $PublicStructName }}Procedur return {{ $PublicStructName }}ProcedureMocker{} } -func (m {{ $PublicStructName }}ProcedureMocker) ByFixtureParams(ctx context.Context, params {{$PackageName}}.FooParams) octopus.FixtureType { +func (m {{ $PublicStructName }}ProcedureMocker) ByFixture{{ if ne $procInLen 0 }}Params{{ end }}(ctx context.Context{{ if ne $procInLen 0 }}, params {{$PackageName}}.{{ $PublicStructName }}Params{{ end }}) octopus.FixtureType { + {{ if ne $procInLen 0 }} return m.ByParamsMocks(ctx, params, []octopus.MockEntities{ Get{{$PublicStructName}}ByParams(params), }) + {{ else }} + return m.ByMocks(ctx, + []octopus.MockEntities{ + Get{{$PublicStructName}}ByParams({{$PackageName}}.{{$PublicStructName}}Params{}), + }) + {{- end }} } -func (m {{ $PublicStructName }}ProcedureMocker) ByParamsMocks(ctx context.Context, params {{$PackageName}}.FooParams, mocks []octopus.MockEntities) octopus.FixtureType { +func (m {{ $PublicStructName }}ProcedureMocker) By{{ if ne $procInLen 0 }}Params{{ end }}Mocks(ctx context.Context{{ if ne $procInLen 0 }}, params {{$PackageName}}.{{ $PublicStructName }}Params{{ end }}, mocks []octopus.MockEntities) octopus.FixtureType { oft, err := octopus.CreateCallFixture( func(wsubME []octopus.MockEntities) []byte { - return {{$PackageName}}.MockCallRequest(ctx, params) + return {{$PackageName}}.MockCallRequest(ctx{{ if ne $procInLen 0 }}, params{{ end }}) }, mocks, ) diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index c407eb7..977b0d6 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -27,6 +27,7 @@ import ( {{ $flags := .Flags }} {{ $fields := .FieldList }} {{ $procfields := .ProcOutFieldList }} +{{ $procInLen := len .ProcInFieldList }} {{ if $fields }} type {{ $PublicStructName }} struct { @@ -126,6 +127,7 @@ func (obj *{{ $PublicStructName }}) setParams(params {{ $PublicStructName }}Par return nil } +{{ if ne $procInLen 0 }} func (obj *{{ $PublicStructName }}Params) arrayValues() []string { return []string{ {{- range $ind, $fstruct := .ProcInFieldList -}} @@ -133,8 +135,9 @@ func (obj *{{ $PublicStructName }}Params) arrayValues() []string { {{- end }} } } +{{ end }} -func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $PublicStructName }}, error) { +func Call(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $PublicStructName }}Params{{ end }}) (*{{ $PublicStructName }}, error) { logger := activerecord.Logger() ctx = logger.SetLoggerValueToContext(ctx, map[string]interface{}{"LuaProc": procName}) metricTimer := activerecord.Metric().Timer("octopus", "{{ $PublicStructName }}") @@ -142,7 +145,7 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi metricTimer.Timing(ctx, "call_proc") - connection, err := box(ctx, 0, activerecord.ReplicaInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.ReplicaInstanceType) if err != nil { metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) logger.Error(ctx, fmt.Sprintf("Error get box '%s'", err)) @@ -150,7 +153,7 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi return nil, err } - td, err := octopus.CallLua(ctx, connection, procName, params.arrayValues()...) + td, err := octopus.CallLua(ctx, connection, procName{{ if ne $procInLen 0 }}, params.arrayValues()...{{ end }}) if err != nil { metricErrCnt.Inc(ctx, "call_proc", 1) return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) @@ -170,7 +173,7 @@ func Call(ctx context.Context, params {{ $PublicStructName }}Params) (*{{ $Publi metricTimer.Finish(ctx, "call_proc") - activerecord.Logger().CollectQueries(ctx, CallMockerLogger(params, {{ $PublicStructName }}List([]*{{ $PublicStructName }}{ret}))) + activerecord.Logger().CollectQueries(ctx, CallMockerLogger({{ if ne $procInLen 0 }}params, {{ end }}{{ $PublicStructName }}List([]*{{ $PublicStructName }}{ret}))) return ret, nil } @@ -276,79 +279,6 @@ var clusterInfo = activerecord.NewClusterInfo( activerecord.WithShard([]activerecord.OptionInterface{boxOption}, []activerecord.OptionInterface{}), ){{ end }} -// box - возвращает коннектор для БД -// TODO -// - унести в пакет pkg/octopus тут общий код нет смысла его нагенеривать -// - сделать статистику по используемым инстансам -// - прикрутить локальный пингер и исключать недоступные инстансы -func box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*octopus.Connection, error) { -{{ if ne .Server.Conf "" -}} - configPath := "{{ .Server.Conf }}" - - clusterInfo, err := activerecord.ConfigCacher().Get( - ctx, - configPath, - activerecord.MapGlobParam{ - Timeout: octopus.DefaultConnectionTimeout, - PoolSize: octopus.DefaultPoolSize, - }, - func(sic activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error) { - return octopus.NewOptions( - sic.Addr, - octopus.ServerModeType(sic.Mode), - octopus.WithTimeout(sic.Timeout, sic.Timeout), - octopus.WithPoolSize(sic.PoolSize), - ) - }, - ) - if err != nil { - return nil, fmt.Errorf("can't get cluster %s info: %w", configPath, err) - } - - if len(clusterInfo) < int(shard) { - return nil, fmt.Errorf("invalid shard num %d, max = %d", shard, len(clusterInfo)) - } -{{ end }} - var configBox activerecord.ShardInstance - - switch instType { - case activerecord.ReplicaInstanceType: - if len(clusterInfo[shard].Replicas) == 0 { - return nil, fmt.Errorf("replicas not set") - } - - configBox = clusterInfo[shard].NextReplica() - case activerecord.ReplicaOrMasterInstanceType: - if len(clusterInfo[shard].Replicas) != 0 { - configBox = clusterInfo[shard].NextReplica() - break - } - - fallthrough - case activerecord.MasterInstanceType: - configBox = clusterInfo[shard].NextMaster() - } - - conn, err := activerecord.ConnectionCacher().GetOrAdd(configBox, func(options interface{}) (activerecord.ConnectionInterface, error) { - octopusOpt, ok := options.(*octopus.ConnectionOptions) - if !ok { - return nil, fmt.Errorf("invalit type of options %T, want Options", options) - } - - return octopus.GetConnection(ctx, octopusOpt) - }) - if err != nil { - return nil, fmt.Errorf("error from connectionCacher: %w", err) - } - - box, ok := conn.(*octopus.Connection) - if !ok { - return nil, fmt.Errorf("invalid connection type %T, want *octopus.Connection", conn) - } - - return box, nil -} - func New(ctx context.Context) *{{ $PublicStructName }} { newObj := {{ $PublicStructName }}{} {{- if $fields }} @@ -675,7 +605,7 @@ func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, lim logger.Debug(ctx, fmt.Sprintf("Select packed tuple: '% X'", w)) - connection, err := box(ctx, 0, activerecord.ReplicaOrMasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.ReplicaOrMasterInstanceType) if err != nil { metricErrCnt.Inc(ctx, "select_preparebox", 1) logger.Error(ctx, fmt.Sprintf("Error get box '%s'", err)) @@ -1069,7 +999,7 @@ func (obj *{{ $PublicStructName }}) Delete(ctx context.Context) error { w := octopus.PackDelete(namespace, pk) log.Printf("Delete packed tuple: '%X'\n", w) - connection, err := box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) if err != nil { metricErrCnt.Inc(ctx, "delete_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) @@ -1146,7 +1076,7 @@ func (obj *{{ $PublicStructName }}) Update(ctx context.Context) error { log.Printf("Update packed tuple: '%X'\n", w) - connection, err := box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) if err != nil { metricErrCnt.Inc(ctx, "update_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) @@ -1268,7 +1198,7 @@ func (obj *{{ $PublicStructName }}) insertReplace(ctx context.Context, insertMod metricTimer.Timing(ctx, "insertreplace_pack") logger.Trace(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Insert packed tuple: '%X'", w)) - connection, err := box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) if err != nil { metricErrCnt.Inc(ctx, "insertreplace_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index ed8ddd0..6085b2a 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -18,6 +18,7 @@ import ( {{ $PublicStructName := .ARPkgTitle -}} {{ $fields := .FieldList }} {{ $procfields := .ProcOutFieldList }} +{{ $procInLen := len .ProcInFieldList }} {{ $serializers := .Serializers -}} {{ $fidx := .FieldMap }} @@ -40,15 +41,15 @@ func (obj *{{ $PublicStructName }}) MockSelectResponse() ([][]byte, error) { return tuple, nil } -func MockCallRequest(ctx context.Context, params {{ $PublicStructName }}Params) []byte { +func MockCallRequest(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $PublicStructName }}Params{{ end }}) []byte { log := activerecord.Logger() - ctx = log.SetLoggerValueToContext(ctx, map[string]interface{}{"MockCallRequest": params, "Proc": "{{ $PublicStructName }}"}) + ctx = log.SetLoggerValueToContext(ctx, map[string]interface{}{ {{ if ne $procInLen 0 }}"MockCallRequest": params, {{ end }}"Proc": "{{ $PublicStructName }}"}) - return octopus.PackLua(procName, params.arrayValues()...) + return octopus.PackLua(procName{{ if ne $procInLen 0 }}, params.arrayValues()...{{ end }}) } func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, error) { - data, err := Call(ctx, obj.params) + data, err := Call(ctx{{ if ne $procInLen 0 }}, obj.params{{ end }}) if err != nil { return nil, err } @@ -60,12 +61,13 @@ func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, erro return data, err } -func CallMockerLogger(params {{ $PublicStructName }}Params, res {{ $PublicStructName }}List) func() (activerecord.MockerLogger, error) { +func CallMockerLogger({{ if ne $procInLen 0 }}params {{ $PublicStructName }}Params, {{ end }}res {{ $PublicStructName }}List) func() (activerecord.MockerLogger, error) { return func() (activerecord.MockerLogger, error){ mockerName := "mocker{{ $PublicStructName }}ByParams" mocker := "fixture.Get{{ $PublicStructName }}ProcedureMocker()" + {{ if ne $procInLen 0 }} fixture := "ps := {{$PublicStructName}}Params{ \n" {{- range $ind, $fstruct := .ProcInFieldList }} fixture += "{{$fstruct.Name}}: params.{{$fstruct.Name}},\n" @@ -73,6 +75,10 @@ return func() (activerecord.MockerLogger, error){ fixture += "}\n" fixture += mocker fixture += ".ByFixtureParams(ctx, ps)" + {{ else }} + fixture := mocker + fixture += ".ByFixture(ctx)" + {{ end }} return activerecord.MockerLogger{MockerName: mockerName, Mockers: mocker, FixturesSelector: fixture, ResultName: "{{ $pkgName }}", Results: res}, nil } diff --git a/pkg/octopus/box.go b/pkg/octopus/box.go index be6c320..6472cf4 100644 --- a/pkg/octopus/box.go +++ b/pkg/octopus/box.go @@ -2,12 +2,85 @@ package octopus import ( "bytes" + "context" "fmt" "io" + "github.com/mailru/activerecord/pkg/activerecord" "github.com/mailru/activerecord/pkg/iproto/iproto" ) +// Box - возвращает коннектор для БД +// TODO +// - сделать статистику по используемым инстансам +// - прикрутить локальный пингер и исключать недоступные инстансы +func Box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*Connection, error) { + configPath := "arcfg" + + clusterInfo, err := activerecord.ConfigCacher().Get( + ctx, + configPath, + activerecord.MapGlobParam{ + Timeout: DefaultConnectionTimeout, + PoolSize: DefaultPoolSize, + }, + func(sic activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error) { + return NewOptions( + sic.Addr, + ServerModeType(sic.Mode), + WithTimeout(sic.Timeout, sic.Timeout), + WithPoolSize(sic.PoolSize), + ) + }, + ) + if err != nil { + return nil, fmt.Errorf("can't get cluster %s info: %w", configPath, err) + } + + if len(clusterInfo) < int(shard) { + return nil, fmt.Errorf("invalid shard num %d, max = %d", shard, len(clusterInfo)) + } + + var configBox activerecord.ShardInstance + + switch instType { + case activerecord.ReplicaInstanceType: + if len(clusterInfo[shard].Replicas) == 0 { + return nil, fmt.Errorf("replicas not set") + } + + configBox = clusterInfo[shard].NextReplica() + case activerecord.ReplicaOrMasterInstanceType: + if len(clusterInfo[shard].Replicas) != 0 { + configBox = clusterInfo[shard].NextReplica() + break + } + + fallthrough + case activerecord.MasterInstanceType: + configBox = clusterInfo[shard].NextMaster() + } + + conn, err := activerecord.ConnectionCacher().GetOrAdd(configBox, func(options interface{}) (activerecord.ConnectionInterface, error) { + octopusOpt, ok := options.(*ConnectionOptions) + if !ok { + return nil, fmt.Errorf("invalit type of options %T, want Options", options) + } + + return GetConnection(ctx, octopusOpt) + }) + if err != nil { + return nil, fmt.Errorf("error from connectionCacher: %w", err) + } + + box, ok := conn.(*Connection) + if !ok { + return nil, fmt.Errorf("invalid connection type %T, want *octopus.Connection", conn) + } + + return box, nil +} + func ProcessResp(respBytes []byte, cntFlag CountFlags) ([]TupleData, error) { tupleCnt, respData, errResp := UnpackResopnseStatus(respBytes) if errResp != nil { From eaa00caf118f49195265b907338a6a691fd4f20d Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Wed, 26 Apr 2023 05:57:56 +0300 Subject: [PATCH 09/20] fix test --- internal/app/argen_b_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/app/argen_b_test.go b/internal/app/argen_b_test.go index 871a108..b47d619 100644 --- a/internal/app/argen_b_test.go +++ b/internal/app/argen_b_test.go @@ -274,7 +274,7 @@ func TestArGen_Run(t *testing.T) { go 1.19 require ( - github.com/mailru/activerecord v0.9.3 + github.com/mailru/activerecord v1.5.4 ) replace github.com/mailru/activerecord => ` + srcPath @@ -319,7 +319,12 @@ import ( func main() { ctx := context.Background() log.Printf("Start") - activerecord.InitActiveRecord() + activerecord.InitActiveRecord( + activerecord.WithConfig(activerecord.NewDefaultConfigFromMap(map[string]interface{}{ + "arcfg/master": "127.0.0.1:11111", + "arcfg/replica": "127.0.0.1:11111", + })), +) ` + gr.testGoMain + ` //activerecord.ConnectionCacher().CloseConnection(ctx) fmt.Print("OK") From 1f3baae573e1551496907013dee3d1fe962698a2 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:18:04 +0300 Subject: [PATCH 10/20] input params serializer support --- internal/pkg/checker/checker.go | 4 +- internal/pkg/generator/fixture_test.go | 4 +- internal/pkg/generator/octopus_b_test.go | 2 +- .../pkg/generator/tmpl/octopus/fixture.tmpl | 6 +++ .../generator/tmpl/octopus/fixturestore.tmpl | 11 +++-- internal/pkg/generator/tmpl/octopus/main.tmpl | 47 ++++++++++++++++--- internal/pkg/generator/tmpl/octopus/mock.tmpl | 9 +++- 7 files changed, 67 insertions(+), 16 deletions(-) diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index 366e92e..83f0aaf 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -121,7 +121,9 @@ func checkFields(cl *ds.RecordPackage) error { } if len(fld.Serializer) > 0 { - return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotSupported} + if _, ex := cl.SerializerMap[fld.Serializer[0]]; !ex { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} + } } if fld.Type == 0 { diff --git a/internal/pkg/generator/fixture_test.go b/internal/pkg/generator/fixture_test.go index 933691a..9321eb6 100644 --- a/internal/pkg/generator/fixture_test.go +++ b/internal/pkg/generator/fixture_test.go @@ -139,7 +139,7 @@ func TestGenerateFixture(t *testing.T) { }, wantStr: map[string][]string{ "fixture": { - `var giftStore map[gift.GiftParams]*gift.Gift`, + `var giftStore map[string]*gift.Gift`, `func initGift() {`, `func GetGiftByParams(params gift.GiftParams) *gift.Gift {`, `type GiftProcedureMocker struct {}`, @@ -207,7 +207,7 @@ func TestGenerateFixture(t *testing.T) { }, wantStr: map[string][]string{ "fixture": { - `var giftStore map[gift.GiftParams]*gift.Gift`, + `var giftStore map[string]*gift.Gift`, `func initGift() {`, `func GetGiftByParams(params gift.GiftParams) *gift.Gift {`, `type GiftProcedureMocker struct {}`, diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 5a33dfb..34cc779 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -244,7 +244,7 @@ func TestGenerateOctopus(t *testing.T) { `procName string = "bar"`, `type Foo struct {`, `type FooParams struct {`, - `func (obj *FooParams) arrayValues() []string`, + `func (obj *FooParams) arrayValues() ([]string, error)`, `package ` + packageName, }, "mock": { diff --git a/internal/pkg/generator/tmpl/octopus/fixture.tmpl b/internal/pkg/generator/tmpl/octopus/fixture.tmpl index cbf0f7d..8897d81 100644 --- a/internal/pkg/generator/tmpl/octopus/fixture.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixture.tmpl @@ -36,6 +36,12 @@ import ( type {{ $PublicStructName }}FTPK struct { {{- range $ind, $fstruct := .ProcInFieldList }} {{ $rtype := $fstruct.Format -}} + {{ $serlen := len $fstruct.Serializer -}} + {{ if ne $serlen 0 -}} + {{ $sname := index $fstruct.Serializer 0 -}} + {{ $serializer := index $serializers $sname -}} + {{ $rtype = $serializer.Type -}} + {{ end }} {{ $fstruct.Name }} {{ $rtype -}} `yaml:"{{ $fstruct.Name | snakeCase -}}"` {{- end }} } diff --git a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl index 40f3680..de49686 100644 --- a/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl +++ b/internal/pkg/generator/tmpl/octopus/fixturestore.tmpl @@ -29,8 +29,9 @@ import ( {{ $fieldNamePK := "" -}} {{ if $procfields }} +{{ $typePK := "string" -}} var {{$PackageName}}Once sync.Once -var {{$PackageName}}Store map[{{$PackageName}}.{{$PublicStructName}}Params]*{{$PackageName}}.{{$PublicStructName}} +var {{$PackageName}}Store map[{{$typePK}}]*{{$PackageName}}.{{$PublicStructName}} //go:embed data/{{$PackageName}}.yaml var {{$PackageName}}Source []byte @@ -39,12 +40,12 @@ func init{{$PublicStructName}}() { {{$PackageName}}Once.Do(func() { fixtures := {{$PackageName}}.UnmarshalFixtures({{$PackageName}}Source) - {{$PackageName}}Store = map[{{$PackageName}}.{{$PublicStructName}}Params]*{{$PackageName}}.{{$PublicStructName}}{} + {{$PackageName}}Store = map[{{$typePK}}]*{{$PackageName}}.{{$PublicStructName}}{} for _, f := range fixtures { - if _, ok := {{$PackageName}}Store[f.GetParams()]; ok { + if _, ok := {{$PackageName}}Store[f.GetParams().PK()]; ok { log.Fatalf("{{$PackageName}} fixture with params %v are duplicated", f.GetParams()) } - {{$PackageName}}Store[f.GetParams()] = f + {{$PackageName}}Store[f.GetParams().PK()] = f } }) } @@ -52,7 +53,7 @@ func init{{$PublicStructName}}() { func Get{{$PublicStructName}}ByParams(params {{$PackageName}}.{{$PublicStructName}}Params) *{{$PackageName}}.{{$PublicStructName}} { init{{$PublicStructName}}() - res, ex := {{$PackageName}}Store[params] + res, ex := {{$PackageName}}Store[params.PK()] if !ex { log.Fatalf("{{$PublicStructName}} fixture with params %v not found", params) } diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 977b0d6..5ba909c 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -128,15 +128,41 @@ func (obj *{{ $PublicStructName }}) setParams(params {{ $PublicStructName }}Par } {{ if ne $procInLen 0 }} -func (obj *{{ $PublicStructName }}Params) arrayValues() []string { - return []string{ -{{- range $ind, $fstruct := .ProcInFieldList -}} - obj.{{- $fstruct.Name -}}, -{{- end }} +func (obj *{{ $PublicStructName }}Params) arrayValues() ([]string, error) { +ret := []string{} +{{ range $ind, $fstruct := .ProcInFieldList -}} + {{ $packerparam := packerParam $fstruct.Format -}} + {{ $sname := $fstruct.Serializer.Name -}} + {{ $bvar := $packerparam.PackConvFunc $fstruct.Name -}} + {{ if ne $sname "" -}} + {{ $serializer := index $serializers $sname -}} + {{ $serparams := $fstruct.Serializer.Params -}} + pvar, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}obj.{{ $bvar }}) + if err != nil { + return nil, fmt.Errorf("error marshal param field {{ $fstruct.Name }}: %w", err) } + + ret = append(ret, pvar...) + {{- else -}} + ret = append(ret, obj.{{ $bvar }}) + {{- end }} +{{ end }} + return ret, nil } {{ end }} +func (obj {{ $PublicStructName }}Params) PK() string { + {{ if ne $procInLen 0 }} + return fmt.Sprint( + {{ range $ind, $fstruct := .ProcInFieldList -}} + obj.{{ $fstruct.Name }}, + {{ end -}} + ) + {{ else }} + return "" + {{ end }} +} + func Call(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $PublicStructName }}Params{{ end }}) (*{{ $PublicStructName }}, error) { logger := activerecord.Logger() ctx = logger.SetLoggerValueToContext(ctx, map[string]interface{}{"LuaProc": procName}) @@ -153,7 +179,16 @@ func Call(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $PublicStructNa return nil, err } - td, err := octopus.CallLua(ctx, connection, procName{{ if ne $procInLen 0 }}, params.arrayValues()...{{ end }}) + var args []string + {{ if ne $procInLen 0 }} + args, err = params.arrayValues() + if err != nil { + metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) + return nil, fmt.Errorf("Error parse args of procedure %s: %w", procName, err) + } + {{ end }} + + td, err := octopus.CallLua(ctx, connection, procName, args...) if err != nil { metricErrCnt.Inc(ctx, "call_proc", 1) return nil, fmt.Errorf("call lua procedure %s: %w", procName, err) diff --git a/internal/pkg/generator/tmpl/octopus/mock.tmpl b/internal/pkg/generator/tmpl/octopus/mock.tmpl index 6085b2a..7b28be3 100644 --- a/internal/pkg/generator/tmpl/octopus/mock.tmpl +++ b/internal/pkg/generator/tmpl/octopus/mock.tmpl @@ -45,7 +45,14 @@ func MockCallRequest(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $Pub log := activerecord.Logger() ctx = log.SetLoggerValueToContext(ctx, map[string]interface{}{ {{ if ne $procInLen 0 }}"MockCallRequest": params, {{ end }}"Proc": "{{ $PublicStructName }}"}) - return octopus.PackLua(procName{{ if ne $procInLen 0 }}, params.arrayValues()...{{ end }}) + {{ if ne $procInLen 0 }} + args, err := params.arrayValues() + if err != nil { + activerecord.Logger().Fatal(ctx, fmt.Sprintf("Error call mock request by params: %s", err)) + } + {{ end }} + + return octopus.PackLua(procName{{ if ne $procInLen 0 }}, args...{{ end }}) } func (obj *{{ $PublicStructName }}) RepoSelector(ctx context.Context) (any, error) { From 44792b574da6a918ff7b0df5d829dc57da9fd621 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Wed, 26 Apr 2023 17:33:58 +0300 Subject: [PATCH 11/20] docs --- docs/manual.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/manual.md b/docs/manual.md index 6695486..ddc8c0f 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -79,7 +79,7 @@ IP адрес или имя хоста, где запущена БД ### namespace -Номер спейса если используется `octopus` (`tarantool 1.5`) +Номер спейса если используется `octopus` (`tarantool 1.5`). При вызове функции/процедуры содержит имя процедуры ### backend @@ -154,6 +154,25 @@ IP адрес или имя хоста, где запущена БД - `fieldnum` - количество используемых полей мульти-колоночного индекса (по умолчанию: 1); - `selector` - имя метода-селектора, который нужно создать для индекса; +### ProcFields* (описание модели вызова функции/процедуры) + +Перечисление входных и выходных параметров сигнатуры функции или хранимой процедуры БД. В тегах у каждого поля возможно указать следующие опции: + +- `input` - обязательный для всех входных параметров в сигнатуре вызова; +- `output` - обязательный для всех выходных параметров в сигнатуре; Для БД поддерживающих inout параметры возможно комбинировать с `input` +- `serializer` - позволяет навесить дополнительную сериализацию на поле; Формат: `Name[,params]`. Параметры необязательные, но если их указать то они будут переданы в функции `marshal`, `unmarshal` +- `size` - длина поля в байтах для возможности валидации (в реализации для octopus не используется) + +Корректное описание требует как минимум одного поля с опцией `output`. +Порядок, в котором перечисляются поля важен и должен строго соответствовать порядку следования в сигнатуре вызова + +#### Для octopus +Порядок, в котором перечисляются поля важен и должен строго соответствовать порядку полей в тупле: + +- номер поля выходного параметра в тупле вычисляется автоматически на основании порядка объявления полей выходных параметров в модуле; +- если кол-во полей в тупле оказывается меньше чем кол-во перечисленных выходных параметров, то оставшиеся поля останутся пустыми + + ### Serializers* Объявление дополнительных сериализаторов для полей. Когда не хватает обычных типов и необходимо работать, например, со словарями, то можно объявить сериализатор, который будет применяться для определённого поля. Тип сериализатора переопределяет тип поля внутри объекта. Допустимые параметры в тегах: @@ -366,6 +385,7 @@ func NewThreshold(limit uint32) Limiter { - `insertreplace_packtuple` - `insertreplace_pack` - `insertreplace_box` + - `call_proc` - статистическим - `insert_success` - `insertorreplace_success` @@ -402,6 +422,8 @@ func NewThreshold(limit uint32) Limiter { - `update_packpk` - `update_preparebox` - `update_resp` + - `call_proc` + - `call_proc_preparebox` ## Пример @@ -436,6 +458,7 @@ type ( ) ``` + ### Запуск генератора `argen --path 'model/repository' --declaration "decl" --destination 'cmpl'` From 780c28bccdef61c6a1a96c12e00d480eb5047920 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:57:42 +0300 Subject: [PATCH 12/20] bigfix gen serializer in template --- docs/manual.md | 8 +++- internal/pkg/arerror/parse.go | 1 + internal/pkg/checker/checker.go | 36 ++++++++++++-- internal/pkg/checker/checker_b_test.go | 29 +++++++++++- internal/pkg/checker/checker_w_test.go | 47 ++++++++++++++----- internal/pkg/generator/octopus_b_test.go | 2 +- internal/pkg/generator/tmpl/octopus/main.tmpl | 13 +++-- internal/pkg/parser/field.go | 18 ++++--- internal/pkg/parser/parser.go | 7 +-- internal/pkg/parser/parser_b_test.go | 4 +- pkg/octopus/types.go | 36 ++++++++------ 11 files changed, 151 insertions(+), 50 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index ddc8c0f..6bc503a 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -167,11 +167,17 @@ IP адрес или имя хоста, где запущена БД Порядок, в котором перечисляются поля важен и должен строго соответствовать порядку следования в сигнатуре вызова #### Для octopus -Порядок, в котором перечисляются поля важен и должен строго соответствовать порядку полей в тупле: +Порядок, в котором перечисляются поля для значений выходных параметров важен и должен строго соответствовать порядку полей в тупле. +- входные параметры могут быть только строкового типа +- входные параметры для списка строк должны иметь сериализатор в строку +- сериализаторы для входных параметров в методе marshal должны возвращать типы string или []string - номер поля выходного параметра в тупле вычисляется автоматически на основании порядка объявления полей выходных параметров в модуле; - если кол-во полей в тупле оказывается меньше чем кол-во перечисленных выходных параметров, то оставшиеся поля останутся пустыми +Для вызова процедур у octopus входные параметры могут быть только строкового типа. Поэтому в качестве типа входного параметра можно указывать только строку (включая []byte) +Но можно объединять входные параметры в структуры используя сериализатор (см. [примеры](https://github.com/mailru/activerecord-cookbook/blob/proc-example/example/model/repository/declaration/foo.go). +Или список при наличии у параметра сериализатора. В этом случае при вызове процедуры будут передаваться строковые параметры в последовательности которую вернет сериализатор ### Serializers* diff --git a/internal/pkg/arerror/parse.go b/internal/pkg/arerror/parse.go index db8a870..9fbad89 100644 --- a/internal/pkg/arerror/parse.go +++ b/internal/pkg/arerror/parse.go @@ -122,6 +122,7 @@ func (e *ErrParseTypeFieldTagDecl) Error() string { } var ErrParseFieldArrayOfNotByte = errors.New("support only array of byte") +var ErrParseProcFieldArraySlice = errors.New("support only array|slice of byte|string") var ErrParseFieldArrayNotSlice = errors.New("only array of byte not a slice") var ErrParseFieldBinary = errors.New("binary format not implemented") var ErrParseFieldMutatorInvalid = errors.New("invalid mutator") diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index 83f0aaf..abc2db9 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -2,6 +2,7 @@ package checker import ( "log" + "strconv" "github.com/mailru/activerecord/internal/pkg/arerror" "github.com/mailru/activerecord/internal/pkg/ds" @@ -65,6 +66,8 @@ func checkNamespace(ns *ds.NamespaceDeclaration) error { // - сериализуемые поля не могут быть ссылками на другие сущности // - есть первичный ключ // - имена сущностей на которые ссылаемся на могут пересекаться с именами полей +// +//nolint:gocyclo func checkFields(cl *ds.RecordPackage) error { if len(cl.Fields) > 0 && len(cl.ProcOutFields) > 0 { return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsManyDecl} @@ -115,7 +118,7 @@ func checkFields(cl *ds.RecordPackage) error { } } - for _, fld := range cl.ProcInFields { + for _, fld := range cl.ProcOutFields { if _, ex := octopusAvailFormat[fld.Format]; !ex { return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} } @@ -131,8 +134,13 @@ func checkFields(cl *ds.RecordPackage) error { } } - for _, fld := range cl.ProcOutFields { - if _, ex := octopusAvailFormat[fld.Format]; !ex { + octopusProcAvailFormat := map[octopus.Format]bool{} + for _, form := range octopus.AllProcFormat { + octopusProcAvailFormat[form] = true + } + + for _, fld := range cl.ProcInFields { + if _, ex := octopusProcAvailFormat[fld.Format]; !ex { return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} } @@ -219,5 +227,27 @@ func checkOctopus(cl *ds.RecordPackage) error { } } + if len(cl.Fields) > 0 { + _, err := strconv.ParseInt(cl.Namespace.ObjectName, 10, 64) + if err != nil { + return &arerror.ErrCheckPackageNamespaceDecl{Pkg: cl.Namespace.PackageName, Name: cl.Namespace.ObjectName, Err: arerror.ErrCheckFieldInvalidFormat} + } + } + + octopusProcAvailFormat := map[octopus.Format]bool{} + for _, form := range []octopus.Format{octopus.String, octopus.StringArray, octopus.ByteArray} { + octopusProcAvailFormat[form] = true + } + + for _, fld := range cl.ProcInFields { + if _, ex := octopusProcAvailFormat[fld.Format]; !ex { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} + } + + if fld.Format != octopus.String && len(fld.Serializer) == 0 { + return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldSerializerNotFound} + } + } + return nil } diff --git a/internal/pkg/checker/checker_b_test.go b/internal/pkg/checker/checker_b_test.go index d65afdf..1ac71db 100644 --- a/internal/pkg/checker/checker_b_test.go +++ b/internal/pkg/checker/checker_b_test.go @@ -61,7 +61,26 @@ func TestCheck(t *testing.T) { err = rpInvalidFormat.AddField(ds.FieldDeclaration{ Name: "ID", - Format: octopus.Format("byte"), + Format: "byte", + PrimaryKey: true, + Mutators: []ds.FieldMutator{}, + Size: 0, + Serializer: []string{}, + ObjectLink: "", + }) + if err != nil { + t.Errorf("can't prepare test data: %s", err) + return + } + + onInvalidFormat := ds.NewRecordPackage() + onInvalidFormat.Backends = []string{"octopus"} + onInvalidFormat.Namespace = ds.NamespaceDeclaration{ObjectName: "invalid", PackageName: "invform", PublicName: "InvalidFormat"} + onInvalidFormat.Server = ds.ServerDeclaration{Host: "127.0.0.1", Port: "11011", Conf: "box"} + + err = onInvalidFormat.AddField(ds.FieldDeclaration{ + Name: "ID", + Format: "byte", PrimaryKey: true, Mutators: []ds.FieldMutator{}, Size: 0, @@ -106,6 +125,14 @@ func TestCheck(t *testing.T) { }, wantErr: true, }, + { + name: "wrong octopus namespace objectname format", + args: args{ + files: map[string]*ds.RecordPackage{"invalid": onInvalidFormat}, + linkedObjects: map[string]string{}, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 15f164e..252b8ee 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -396,6 +396,7 @@ func Test_checkProcFields(t *testing.T) { ProcOutFields: []ds.ProcFieldDeclaration{ { Name: "Foo", + Type: ds.OUT, }, }, }, @@ -403,13 +404,36 @@ func Test_checkProcFields(t *testing.T) { wantErr: true, }, { - name: "invalid format", + name: "invalid input format", args: args{ cl: ds.RecordPackage{ ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: ds.OUT, + }, + }, + ProcInFields: []ds.ProcFieldDeclaration{ { Name: "Foo", Format: "[]int", + Type: ds.IN, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "invalid output format", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "[]int", + Type: ds.OUT, }, }, }, @@ -446,25 +470,26 @@ func Test_checkProcFields(t *testing.T) { wantErr: false, }, { - name: "serializer not supported", + name: "normal input field", args: args{ cl: ds.RecordPackage{ ProcInFields: []ds.ProcFieldDeclaration{ { Name: "Foo", - Format: "int", + Format: "[]string", Type: ds.IN, - Serializer: []string{ - "fser", - }, }, }, - SerializerMap: map[string]ds.SerializerDeclaration{ - "fser": {}, + ProcOutFields: []ds.ProcFieldDeclaration{ + { + Name: "Foo", + Format: "int", + Type: ds.OUT, + }, }, }, }, - wantErr: true, + wantErr: false, }, { name: "serializer not declared", @@ -474,12 +499,12 @@ func Test_checkProcFields(t *testing.T) { { Name: "Foo", Format: "int", - Type: 2, + Type: ds.OUT, }, { Name: "Foo", Format: "int", - Type: 2, + Type: ds.OUT, Serializer: []string{ "fser", }, diff --git a/internal/pkg/generator/octopus_b_test.go b/internal/pkg/generator/octopus_b_test.go index 34cc779..d2be1d1 100644 --- a/internal/pkg/generator/octopus_b_test.go +++ b/internal/pkg/generator/octopus_b_test.go @@ -190,7 +190,7 @@ func TestGenerateOctopus(t *testing.T) { ProcInFieldList: []ds.ProcFieldDeclaration{ { Name: "Input", - Format: "string", + Format: "[]string", Type: ds.IN, Serializer: []string{}, }, diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 5ba909c..7b1ddb2 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -131,20 +131,23 @@ func (obj *{{ $PublicStructName }}) setParams(params {{ $PublicStructName }}Par func (obj *{{ $PublicStructName }}Params) arrayValues() ([]string, error) { ret := []string{} {{ range $ind, $fstruct := .ProcInFieldList -}} - {{ $packerparam := packerParam $fstruct.Format -}} {{ $sname := $fstruct.Serializer.Name -}} - {{ $bvar := $packerparam.PackConvFunc $fstruct.Name -}} + {{ $bvar := $fstruct.Name -}} {{ if ne $sname "" -}} {{ $serializer := index $serializers $sname -}} {{ $serparams := $fstruct.Serializer.Params -}} - pvar, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}obj.{{ $bvar }}) + pvar{{ $fstruct.Name }}, err := {{ $serializer.ImportName }}.{{ $serializer.Marshaler }}({{ $serparams }}obj.{{ $bvar }}) if err != nil { return nil, fmt.Errorf("error marshal param field {{ $fstruct.Name }}: %w", err) } - ret = append(ret, pvar...) + {{ if eq $fstruct.Format "[]string" }} + ret = append(ret, pvar{{ $fstruct.Name }}...) + {{ else }} + ret = append(ret, string(pvar{{ $fstruct.Name }})) + {{- end }} {{- else -}} - ret = append(ret, obj.{{ $bvar }}) + ret = append(ret, string(obj.{{ $fstruct.Name }})) {{- end }} {{ end }} return ret, nil diff --git a/internal/pkg/parser/field.go b/internal/pkg/parser/field.go index 2a6c98c..ae0cbf2 100644 --- a/internal/pkg/parser/field.go +++ b/internal/pkg/parser/field.go @@ -166,12 +166,22 @@ func ParseProcFields(dst *ds.RecordPackage, fields []*ast.Field) error { Serializer: []string{}, } + if err := ParseProcFieldsTag(field, &newField); err != nil { + return fmt.Errorf("error ParseFieldsTag: %w", err) + } + switch t := field.Type.(type) { case *ast.Ident: newField.Format = octopus.Format(t.String()) case *ast.ArrayType: - if t.Elt.(*ast.Ident).Name != "byte" { - return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: t.Elt.(*ast.Ident).Name, Err: arerror.ErrParseFieldArrayOfNotByte} + if t.Elt.(*ast.Ident).Name != "byte" && t.Elt.(*ast.Ident).Name != "string" { + return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: t.Elt.(*ast.Ident).Name, Err: arerror.ErrParseProcFieldArraySlice} + } + + // если входной параметр slice + if newField.Type == ds.IN && t.Len == nil { + newField.Format = octopus.Format(fmt.Sprintf("[]%s", t.Elt.(*ast.Ident).Name)) + break } if t.Len == nil { @@ -183,10 +193,6 @@ func ParseProcFields(dst *ds.RecordPackage, fields []*ast.Field) error { return &arerror.ErrParseTypeFieldDecl{Name: newField.Name, FieldType: fmt.Sprintf("%T", t), Err: arerror.ErrUnknown} } - if err := ParseProcFieldsTag(field, &newField); err != nil { - return fmt.Errorf("error ParseFieldsTag: %w", err) - } - if err := dst.AddProcField(newField); err != nil { return err } diff --git a/internal/pkg/parser/parser.go b/internal/pkg/parser/parser.go index b8bc9ab..b923652 100644 --- a/internal/pkg/parser/parser.go +++ b/internal/pkg/parser/parser.go @@ -228,12 +228,7 @@ func parseDoc(dst *ds.RecordPackage, nodeName string, doc *ast.CommentGroup) err case "namespace": switch StructNameType(nodeName) { case Fields: - _, err := strconv.ParseInt(kv[1], 10, 64) - if err != nil { - return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} - } - - dst.Namespace.ObjectName = kv[1] + fallthrough case ProcFields: if len(kv[1]) == 0 { return &arerror.ErrParseDocDecl{Name: kv[0], Value: kv[1], Err: arerror.ErrParseDocNamespaceDecl} diff --git a/internal/pkg/parser/parser_b_test.go b/internal/pkg/parser/parser_b_test.go index 71a23d9..8fe7a37 100644 --- a/internal/pkg/parser/parser_b_test.go +++ b/internal/pkg/parser/parser_b_test.go @@ -158,7 +158,7 @@ func TestParseProc(t *testing.T) { //ar:namespace:bar //ar:backend:octopus type ProcFieldsFoo struct { - InParams1 string ` + "`" + `ar:"input"` + "`" + ` + InParams1 []string ` + "`" + `ar:"input"` + "`" + ` InOutParams2 string ` + "`" + `ar:"input;output"` + "`" + ` Output string ` + "`" + `ar:"output"` + "`" + ` } @@ -205,7 +205,7 @@ type ProcFieldsFoo struct { Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, ProcInFields: []ds.ProcFieldDeclaration{ - {Name: "InParams1", Format: "string", Type: 1, Serializer: []string{}}, + {Name: "InParams1", Format: "[]string", Type: 1, Serializer: []string{}}, {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, }, ProcOutFields: []ds.ProcFieldDeclaration{ diff --git a/pkg/octopus/types.go b/pkg/octopus/types.go index da16136..9f90f77 100644 --- a/pkg/octopus/types.go +++ b/pkg/octopus/types.go @@ -122,20 +122,22 @@ const ( ) const ( - Uint8 Format = "uint8" - Uint16 Format = "uint16" - Uint32 Format = "uint32" - Uint64 Format = "uint64" - Uint Format = "uint" - Int8 Format = "int8" - Int16 Format = "int16" - Int32 Format = "int32" - Int64 Format = "int64" - Int Format = "int" - String Format = "string" - Bool Format = "bool" - Float32 Format = "float32" - Float64 Format = "float64" + Uint8 Format = "uint8" + Uint16 Format = "uint16" + Uint32 Format = "uint32" + Uint64 Format = "uint64" + Uint Format = "uint" + Int8 Format = "int8" + Int16 Format = "int16" + Int32 Format = "int32" + Int64 Format = "int64" + Int Format = "int" + String Format = "string" + Bool Format = "bool" + Float32 Format = "float32" + Float64 Format = "float64" + StringArray Format = "[]string" + ByteArray Format = "[]byte" ) var UnsignedFormat = []Format{Uint8, Uint16, Uint32, Uint64, Uint} @@ -148,6 +150,12 @@ var AllFormat = append(append(append( DataFormat...), Bool, ) +var AllProcFormat = append(append(append( + NumericFormat, + FloatFormat...), + DataFormat...), + Bool, StringArray, ByteArray, +) func GetOpCodeName(op OpCode) string { switch op { From 46378f393b3891c719bb4786969e65ddb0d8ed73 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:09:56 +0300 Subject: [PATCH 13/20] exclude proc spaces from metaspace (avoid duplicate) --- internal/pkg/generator/tmpl/meta.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/pkg/generator/tmpl/meta.tmpl b/internal/pkg/generator/tmpl/meta.tmpl index ec3e5e4..ad169a6 100644 --- a/internal/pkg/generator/tmpl/meta.tmpl +++ b/internal/pkg/generator/tmpl/meta.tmpl @@ -33,6 +33,7 @@ func (ns NSPackage) meta(n uint32) (SpaceMeta, bool) { {{ $nss := .Namespaces }} var NamespacePackages = NSPackage { {{ range $_, $ns := $nss -}} + {{ if $ns.Fields }} "{{ $ns.Namespace.ObjectName }}": { PackageName: "{{ $ns.Namespace.PackageName }}", Unpacker: func(ctx context.Context, tuple octopus.TupleData) (any, error) { @@ -71,6 +72,7 @@ var NamespacePackages = NSPackage { {{- end }} }, }, + {{ end }} {{ end }} } From 5ac944475b82fce05cca41878ba7de228d800279 Mon Sep 17 00:00:00 2001 From: Eugeny <15173395+ebirukov@users.noreply.github.com> Date: Sun, 30 Apr 2023 09:19:15 +0300 Subject: [PATCH 14/20] Update internal/pkg/arerror/parse.go --- internal/pkg/arerror/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/arerror/parse.go b/internal/pkg/arerror/parse.go index 9fbad89..609768e 100644 --- a/internal/pkg/arerror/parse.go +++ b/internal/pkg/arerror/parse.go @@ -122,7 +122,7 @@ func (e *ErrParseTypeFieldTagDecl) Error() string { } var ErrParseFieldArrayOfNotByte = errors.New("support only array of byte") -var ErrParseProcFieldArraySlice = errors.New("support only array|slice of byte|string") +var ErrParseProcFieldArraySlice = errors.New("support array|slice of byte|string") var ErrParseFieldArrayNotSlice = errors.New("only array of byte not a slice") var ErrParseFieldBinary = errors.New("binary format not implemented") var ErrParseFieldMutatorInvalid = errors.New("invalid mutator") From 405aa0ce7213ffd1b0f630abb6730d2b0bf81615 Mon Sep 17 00:00:00 2001 From: Eugeny <15173395+ebirukov@users.noreply.github.com> Date: Sun, 30 Apr 2023 09:19:28 +0300 Subject: [PATCH 15/20] Update internal/pkg/ds/package.go --- internal/pkg/ds/package.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/ds/package.go b/internal/pkg/ds/package.go index bb2efba..b80e3d2 100644 --- a/internal/pkg/ds/package.go +++ b/internal/pkg/ds/package.go @@ -41,7 +41,7 @@ func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { return &arerror.ErrParseTypeFieldDecl{Name: f.Name, FieldType: string(f.Format), Err: arerror.ErrRedefined} } - // добавляем поле и не забываем про обратны индекс + // добавляем поле и не забываем про обратный индекс rc.ProcFieldsMap[f.Name] = len(rc.ProcFieldsMap) // добавляем поле во входные параметры if f.Type == IN || f.Type == INOUT { From 5057b570c00ddd56d419a2fc79a818217038f126 Mon Sep 17 00:00:00 2001 From: Eugeny <15173395+ebirukov@users.noreply.github.com> Date: Sun, 30 Apr 2023 09:19:38 +0300 Subject: [PATCH 16/20] Update internal/pkg/ds/app.go --- internal/pkg/ds/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 150fa71..2f161b7 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -150,7 +150,7 @@ type FieldDeclaration struct { PrimaryKey bool // участвует ли поле в первичном ключе (при изменении таких полей необходимо делать delete + insert вместо update) Mutators []FieldMutator // список мутаторов (атомарных действий на уровне БД) Size int64 // Размер поля, используется только для строковых значений - Serializer Serializer // Сериализатора для поля + Serializer Serializer // Сериализаторы для поля ObjectLink string // является ли поле ссылкой на другую сущность } From de4eb9f6856aee4242e7c6cc20f8409ab7aaf365 Mon Sep 17 00:00:00 2001 From: "e.birukov" Date: Sun, 30 Apr 2023 20:30:36 +0300 Subject: [PATCH 17/20] fix by review (#6): - output proc fields order index - fix octopus box config --- internal/app/argen_w_test.go | 1 + internal/pkg/arerror/checker.go | 1 + internal/pkg/arerror/parse.go | 1 + internal/pkg/checker/checker.go | 6 +- internal/pkg/checker/checker_w_test.go | 54 +++++++++----- internal/pkg/ds/app.go | 70 ++++++++++++++++++- internal/pkg/ds/package.go | 6 +- internal/pkg/generator/generator.go | 2 +- internal/pkg/generator/tmpl/octopus/main.tmpl | 10 +-- internal/pkg/parser/field.go | 14 +++- internal/pkg/parser/import_w_test.go | 1 + internal/pkg/parser/parser_b_test.go | 13 ++-- internal/pkg/parser/parser_w_test.go | 2 + pkg/octopus/box.go | 24 ++++--- 14 files changed, 158 insertions(+), 47 deletions(-) diff --git a/internal/app/argen_w_test.go b/internal/app/argen_w_test.go index d6ed714..089421d 100644 --- a/internal/app/argen_w_test.go +++ b/internal/app/argen_w_test.go @@ -679,6 +679,7 @@ type TriggersFoo struct { }, FieldsMap: map[string]int{"Field1": 0, "Field2": 1}, FieldsObjectMap: map[string]ds.FieldObject{}, + ProcOutFields: map[int]ds.ProcFieldDeclaration{}, ProcFieldsMap: map[string]int{}, Indexes: []ds.IndexDeclaration{ { diff --git a/internal/pkg/arerror/checker.go b/internal/pkg/arerror/checker.go index 390f18d..91c4481 100644 --- a/internal/pkg/arerror/checker.go +++ b/internal/pkg/arerror/checker.go @@ -23,6 +23,7 @@ var ErrCheckObjectNotFound = errors.New("linked object not found") var ErrCheckFieldTypeNotFound = errors.New("procedure field type not found") var ErrCheckFieldsEmpty = errors.New("empty required field declaration") var ErrCheckFieldsManyDecl = errors.New("few declarations of fields not supported") +var ErrCheckFieldsOrderDecl = errors.New("incorrect order of fields") // Описание ошибки декларации пакета type ErrCheckPackageDecl struct { diff --git a/internal/pkg/arerror/parse.go b/internal/pkg/arerror/parse.go index 609768e..c8ce9cc 100644 --- a/internal/pkg/arerror/parse.go +++ b/internal/pkg/arerror/parse.go @@ -14,6 +14,7 @@ var ErrIndexNotExist = errors.New("index not exists") var ErrParseNodeNameUnknown = errors.New("unknown node name") var ErrParseNodeNameInvalid = errors.New("invalid struct name") var ErrParseFuncDeclNotSupported = errors.New("func declaration not implemented") +var ErrProcFieldDuplicateOrderIndex = errors.New("field order index is duplicate") // Описание ошибки парсинга type ErrParseGenDecl struct { diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index abc2db9..6720cd6 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -73,6 +73,10 @@ func checkFields(cl *ds.RecordPackage) error { return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsManyDecl} } + if !cl.ProcOutFields.Validate() { + return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsOrderDecl} + } + primaryFound := false octopusAvailFormat := map[octopus.Format]bool{} @@ -118,7 +122,7 @@ func checkFields(cl *ds.RecordPackage) error { } } - for _, fld := range cl.ProcOutFields { + for _, fld := range cl.ProcOutFields.List() { if _, ex := octopusAvailFormat[fld.Format]; !ex { return &arerror.ErrCheckPackageFieldDecl{Pkg: cl.Namespace.PackageName, Field: fld.Name, Err: arerror.ErrCheckFieldInvalidFormat} } diff --git a/internal/pkg/checker/checker_w_test.go b/internal/pkg/checker/checker_w_test.go index 252b8ee..9e509ad 100644 --- a/internal/pkg/checker/checker_w_test.go +++ b/internal/pkg/checker/checker_w_test.go @@ -362,7 +362,7 @@ func Test_checkProcFields(t *testing.T) { name: "empty fields", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{}, + ProcOutFields: ds.ProcFieldDeclarations{}, }, }, wantErr: true, @@ -378,8 +378,8 @@ func Test_checkProcFields(t *testing.T) { PrimaryKey: true, }, }, - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", Type: ds.INOUT, @@ -393,8 +393,8 @@ func Test_checkProcFields(t *testing.T) { name: "empty format", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Type: ds.OUT, }, @@ -407,8 +407,8 @@ func Test_checkProcFields(t *testing.T) { name: "invalid input format", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", Type: ds.OUT, @@ -429,8 +429,8 @@ func Test_checkProcFields(t *testing.T) { name: "invalid output format", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "[]int", Type: ds.OUT, @@ -444,11 +444,29 @@ func Test_checkProcFields(t *testing.T) { name: "type not found", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { + Name: "Foo", + Format: "int", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "incorrect fields order", + args: args{ + cl: ds.RecordPackage{ + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", }, + 2: { + Name: "Bar", + Format: "int", + }, }, }, }, @@ -458,8 +476,8 @@ func Test_checkProcFields(t *testing.T) { name: "normal field", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", Type: ds.OUT, @@ -480,8 +498,8 @@ func Test_checkProcFields(t *testing.T) { Type: ds.IN, }, }, - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", Type: ds.OUT, @@ -495,13 +513,13 @@ func Test_checkProcFields(t *testing.T) { name: "serializer not declared", args: args{ cl: ds.RecordPackage{ - ProcOutFields: []ds.ProcFieldDeclaration{ - { + ProcOutFields: ds.ProcFieldDeclarations{ + 0: { Name: "Foo", Format: "int", Type: ds.OUT, }, - { + 1: { Name: "Foo", Format: "int", Type: ds.OUT, diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index 2f161b7..ecea74f 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/mailru/activerecord/internal/pkg/arerror" "github.com/mailru/activerecord/pkg/octopus" ) @@ -87,7 +88,7 @@ type RecordPackage struct { TriggerMap map[string]TriggerDeclaration // Список триггеров используемых в сущности FlagMap map[string]FlagDeclaration // Список флагов используемых в полях сущности ProcInFields []ProcFieldDeclaration // Описание входных параметров процедуры, важна последовательность - ProcOutFields []ProcFieldDeclaration // Описание выходных параметров процедуры, важна последовательность + ProcOutFields ProcFieldDeclarations // Описание выходных параметров процедуры, важна последовательность ProcFieldsMap map[string]int // Обратный индекс от имен } @@ -110,6 +111,7 @@ func NewRecordPackage() *RecordPackage { TriggerMap: map[string]TriggerDeclaration{}, FlagMap: map[string]FlagDeclaration{}, ProcFieldsMap: map[string]int{}, + ProcOutFields: map[int]ProcFieldDeclaration{}, } } @@ -173,8 +175,26 @@ func (s Serializer) Params() string { return "" } +const ( + ProcInputParam = "input" + ProcOutputParam = "output" +) + type ProcParameterType uint8 +func (p ProcParameterType) String() string { + switch p { + case IN: + return ProcInputParam + case OUT: + return ProcOutputParam + case INOUT: + return fmt.Sprintf("%v/%v", ProcInputParam, ProcOutputParam) + default: + return "" + } +} + const ( _ ProcParameterType = iota IN //тип входного параметра процедуры @@ -189,6 +209,54 @@ type ProcFieldDeclaration struct { Type ProcParameterType // тип параметра (IN, OUT, INOUT) Size int64 // Размер поля, используется только для строковых значений Serializer Serializer // Сериализатора для поля + OrderIndex int // Порядковый номер параметра в сигнатуре вызова процедуры +} + +// ProcFieldDeclarations Индекс порядкового значения полей процедуры +type ProcFieldDeclarations map[int]ProcFieldDeclaration + +// Add Добавляет декларацию поля процедуры в список. +// Возвращает ошибку, если декларация с таким порядком в [ProcFieldDeclarations] уже существует +func (pfd ProcFieldDeclarations) Add(field ProcFieldDeclaration) error { + idx := field.OrderIndex + + if _, ok := pfd[idx]; ok { + return arerror.ErrProcFieldDuplicateOrderIndex + } + + pfd[idx] = field + + return nil +} + +// List список деклараций процедуры в описанном порядке описания +func (pfd ProcFieldDeclarations) List() []ProcFieldDeclaration { + out := make([]ProcFieldDeclaration, len(pfd)) + for i := range pfd { + out[i] = pfd[i] + } + + return out +} + +// Validate проверяет корректность декларируемых значений порядкового номера полей процедуры +func (pfd ProcFieldDeclarations) Validate() bool { + if len(pfd) == 0 { + return true + } + + var maxIdx int + for idx := range pfd { + if idx > maxIdx { + maxIdx = idx + } + } + + if maxIdx >= len(pfd) { + return false + } + + return true } // Тип и константы описывающие мутаторы для поля diff --git a/internal/pkg/ds/package.go b/internal/pkg/ds/package.go index b80e3d2..35b4013 100644 --- a/internal/pkg/ds/package.go +++ b/internal/pkg/ds/package.go @@ -49,7 +49,9 @@ func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { } // добавляем поле в выходные параметры if f.Type == OUT || f.Type == INOUT { - rc.ProcOutFields = append(rc.ProcOutFields, f) + if err := rc.ProcOutFields.Add(f); err != nil { + return &arerror.ErrParseTypeFieldDecl{Name: f.Name, FieldType: f.Type.String(), Err: err} + } } return nil @@ -58,7 +60,7 @@ func (rc *RecordPackage) AddProcField(f ProcFieldDeclaration) error { // Добавление нового ссылочного поля func (rc *RecordPackage) AddFieldObject(fo FieldObject) error { if _, ex := rc.FieldsObjectMap[fo.Name]; ex { - return &arerror.ErrParseTypeFieldStructDecl{Name: fo.Name, Err: arerror.ErrRedefined} + return &arerror.ErrParseTypeFieldDecl{Name: fo.Name, Err: arerror.ErrRedefined} } rc.FieldsObjectMap[fo.Name] = fo diff --git a/internal/pkg/generator/generator.go b/internal/pkg/generator/generator.go index 76e6364..c60757d 100644 --- a/internal/pkg/generator/generator.go +++ b/internal/pkg/generator/generator.go @@ -55,7 +55,7 @@ func NewPkgData(appInfo string, cl ds.RecordPackage) PkgData { FieldList: cl.Fields, FieldMap: cl.FieldsMap, ProcInFieldList: cl.ProcInFields, - ProcOutFieldList: cl.ProcOutFields, + ProcOutFieldList: cl.ProcOutFields.List(), FieldObject: cl.FieldsObjectMap, Server: cl.Server, Container: cl.Namespace, diff --git a/internal/pkg/generator/tmpl/octopus/main.tmpl b/internal/pkg/generator/tmpl/octopus/main.tmpl index 7b1ddb2..f9ddb88 100644 --- a/internal/pkg/generator/tmpl/octopus/main.tmpl +++ b/internal/pkg/generator/tmpl/octopus/main.tmpl @@ -174,7 +174,7 @@ func Call(ctx context.Context{{ if ne $procInLen 0 }}, params {{ $PublicStructNa metricTimer.Timing(ctx, "call_proc") - connection, err := octopus.Box(ctx, 0, activerecord.ReplicaInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.ReplicaInstanceType, "arcfg", nil) if err != nil { metricErrCnt.Inc(ctx, "call_proc_preparebox", 1) logger.Error(ctx, fmt.Sprintf("Error get box '%s'", err)) @@ -643,7 +643,7 @@ func selectBox (ctx context.Context, indexnum uint32, keysPacked [][][]byte, lim logger.Debug(ctx, fmt.Sprintf("Select packed tuple: '% X'", w)) - connection, err := octopus.Box(ctx, 0, activerecord.ReplicaOrMasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.ReplicaOrMasterInstanceType, "arcfg", nil) if err != nil { metricErrCnt.Inc(ctx, "select_preparebox", 1) logger.Error(ctx, fmt.Sprintf("Error get box '%s'", err)) @@ -1037,7 +1037,7 @@ func (obj *{{ $PublicStructName }}) Delete(ctx context.Context) error { w := octopus.PackDelete(namespace, pk) log.Printf("Delete packed tuple: '%X'\n", w) - connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType, "arcfg", nil) if err != nil { metricErrCnt.Inc(ctx, "delete_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) @@ -1114,7 +1114,7 @@ func (obj *{{ $PublicStructName }}) Update(ctx context.Context) error { log.Printf("Update packed tuple: '%X'\n", w) - connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType, "arcfg", nil) if err != nil { metricErrCnt.Inc(ctx, "update_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) @@ -1236,7 +1236,7 @@ func (obj *{{ $PublicStructName }}) insertReplace(ctx context.Context, insertMod metricTimer.Timing(ctx, "insertreplace_pack") logger.Trace(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Insert packed tuple: '%X'", w)) - connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType) + connection, err := octopus.Box(ctx, 0, activerecord.MasterInstanceType, "arcfg", nil) if err != nil { metricErrCnt.Inc(ctx, "insertreplace_preparebox", 1) logger.Error(ctx, "{{ $PublicStructName }}", obj.PrimaryString(), fmt.Sprintf("Error get box '%s'", err)) diff --git a/internal/pkg/parser/field.go b/internal/pkg/parser/field.go index ae0cbf2..d0ad057 100644 --- a/internal/pkg/parser/field.go +++ b/internal/pkg/parser/field.go @@ -119,7 +119,7 @@ func ParseFields(dst *ds.RecordPackage, fields []*ast.Field) error { } // ParseProcFieldsTag парсинг тегов полей декларации процедуры -func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) error { +func ParseProcFieldsTag(index int, field *ast.Field, newfield *ds.ProcFieldDeclaration) error { tagParam, err := splitTag(field, NoCheckFlag, map[TagNameType]ParamValueRule{PrimaryKeyTag: ParamNotNeedValue, UniqueTag: ParamNotNeedValue}) if err != nil { return &arerror.ErrParseTypeFieldDecl{Name: newfield.Name, FieldType: string(newfield.Format), Err: err} @@ -134,6 +134,16 @@ func ParseProcFieldsTag(field *ast.Field, newfield *ds.ProcFieldDeclaration) err case ProcOutputParamTag: //результат бинарной операции 0|OUT => OUT; 1|OUT => INOUT (3); 2|OUT => OUT; newfield.Type = newfield.Type | ds.OUT + orderIdx := index + + if len(kv) == 2 { + orderIdx, err = strconv.Atoi(kv[1]) + if err != nil { + return &arerror.ErrParseTypeFieldTagDecl{Name: newfield.Name, TagName: kv[0], TagValue: kv[1], Err: arerror.ErrParseTagValueInvalid} + } + } + + newfield.OrderIndex = orderIdx case SizeTag: if kv[1] != "" { size, err := strconv.ParseInt(kv[1], 10, 64) @@ -166,7 +176,7 @@ func ParseProcFields(dst *ds.RecordPackage, fields []*ast.Field) error { Serializer: []string{}, } - if err := ParseProcFieldsTag(field, &newField); err != nil { + if err := ParseProcFieldsTag(len(dst.ProcOutFields), field, &newField); err != nil { return fmt.Errorf("error ParseFieldsTag: %w", err) } diff --git a/internal/pkg/parser/import_w_test.go b/internal/pkg/parser/import_w_test.go index 8a3a5e4..5a5a075 100644 --- a/internal/pkg/parser/import_w_test.go +++ b/internal/pkg/parser/import_w_test.go @@ -43,6 +43,7 @@ func TestParseImport(t *testing.T) { }, Backends: []string{}, ProcFieldsMap: map[string]int{}, + ProcOutFields: map[int]ds.ProcFieldDeclaration{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, diff --git a/internal/pkg/parser/parser_b_test.go b/internal/pkg/parser/parser_b_test.go index 8fe7a37..7b76a11 100644 --- a/internal/pkg/parser/parser_b_test.go +++ b/internal/pkg/parser/parser_b_test.go @@ -133,6 +133,7 @@ type TriggersFoo struct { }, FlagMap: map[string]ds.FlagDeclaration{}, ProcFieldsMap: map[string]int{}, + ProcOutFields: map[int]ds.ProcFieldDeclaration{}, }, }, } @@ -159,8 +160,8 @@ func TestParseProc(t *testing.T) { //ar:backend:octopus type ProcFieldsFoo struct { InParams1 []string ` + "`" + `ar:"input"` + "`" + ` - InOutParams2 string ` + "`" + `ar:"input;output"` + "`" + ` - Output string ` + "`" + `ar:"output"` + "`" + ` + InOutParams2 string ` + "`" + `ar:"input;output:1"` + "`" + ` + Output string ` + "`" + `ar:"output:0"` + "`" + ` } ` @@ -206,11 +207,11 @@ type ProcFieldsFoo struct { FieldsMap: map[string]int{}, ProcInFields: []ds.ProcFieldDeclaration{ {Name: "InParams1", Format: "[]string", Type: 1, Serializer: []string{}}, - {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, + {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}, OrderIndex: 1}, }, - ProcOutFields: []ds.ProcFieldDeclaration{ - {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}}, - {Name: "Output", Format: "string", Type: 2, Serializer: []string{}}, + ProcOutFields: ds.ProcFieldDeclarations{ + 1: {Name: "InOutParams2", Format: "string", Type: 3, Serializer: []string{}, OrderIndex: 1}, + 0: {Name: "Output", Format: "string", Type: 2, Serializer: []string{}, OrderIndex: 0}, }, ProcFieldsMap: map[string]int{"InOutParams2": 1, "InParams1": 0, "Output": 2}, FieldsObjectMap: map[string]ds.FieldObject{}, diff --git a/internal/pkg/parser/parser_w_test.go b/internal/pkg/parser/parser_w_test.go index 7da92f2..edf7361 100644 --- a/internal/pkg/parser/parser_w_test.go +++ b/internal/pkg/parser/parser_w_test.go @@ -50,6 +50,7 @@ func Test_parseDoc(t *testing.T) { Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, ProcFieldsMap: map[string]int{}, + ProcOutFields: map[int]ds.ProcFieldDeclaration{}, FieldsObjectMap: map[string]ds.FieldObject{}, Indexes: []ds.IndexDeclaration{}, IndexMap: map[string]int{}, @@ -367,6 +368,7 @@ func Test_parseAst(t *testing.T) { Server: ds.ServerDeclaration{Timeout: 500, Host: "127.0.0.1", Port: "11011"}, Namespace: ds.NamespaceDeclaration{ObjectName: "5", PublicName: "Baz", PackageName: "baz"}, ProcFieldsMap: map[string]int{}, + ProcOutFields: map[int]ds.ProcFieldDeclaration{}, Fields: []ds.FieldDeclaration{}, FieldsMap: map[string]int{}, FieldsObjectMap: map[string]ds.FieldObject{}, diff --git a/pkg/octopus/box.go b/pkg/octopus/box.go index 6472cf4..39ff43b 100644 --- a/pkg/octopus/box.go +++ b/pkg/octopus/box.go @@ -14,8 +14,17 @@ import ( // TODO // - сделать статистику по используемым инстансам // - прикрутить локальный пингер и исключать недоступные инстансы -func Box(ctx context.Context, shard int, instType activerecord.ShardInstanceType) (*Connection, error) { - configPath := "arcfg" +func Box(ctx context.Context, shard int, instType activerecord.ShardInstanceType, configPath string, optionCreator func(activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error)) (*Connection, error) { + if optionCreator == nil { + optionCreator = func(sic activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error) { + return NewOptions( + sic.Addr, + ServerModeType(sic.Mode), + WithTimeout(sic.Timeout, sic.Timeout), + WithPoolSize(sic.PoolSize), + ) + } + } clusterInfo, err := activerecord.ConfigCacher().Get( ctx, @@ -24,20 +33,13 @@ func Box(ctx context.Context, shard int, instType activerecord.ShardInstanceType Timeout: DefaultConnectionTimeout, PoolSize: DefaultPoolSize, }, - func(sic activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error) { - return NewOptions( - sic.Addr, - ServerModeType(sic.Mode), - WithTimeout(sic.Timeout, sic.Timeout), - WithPoolSize(sic.PoolSize), - ) - }, + optionCreator, ) if err != nil { return nil, fmt.Errorf("can't get cluster %s info: %w", configPath, err) } - if len(clusterInfo) < int(shard) { + if len(clusterInfo) < shard { return nil, fmt.Errorf("invalid shard num %d, max = %d", shard, len(clusterInfo)) } From a3ef830898e571bbdd4ebcf41c38b7689af3c543 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Tue, 2 May 2023 09:17:56 +0300 Subject: [PATCH 18/20] fix linter warn --- internal/pkg/ds/app.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/pkg/ds/app.go b/internal/pkg/ds/app.go index ecea74f..4c25b15 100644 --- a/internal/pkg/ds/app.go +++ b/internal/pkg/ds/app.go @@ -252,11 +252,7 @@ func (pfd ProcFieldDeclarations) Validate() bool { } } - if maxIdx >= len(pfd) { - return false - } - - return true + return maxIdx < len(pfd) } // Тип и константы описывающие мутаторы для поля From b9987df5c43cfd07808be1f6f488b7ec8768a6b3 Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Tue, 2 May 2023 09:29:35 +0300 Subject: [PATCH 19/20] fix gocognit linter warn --- .golangci.yml | 2 +- docs/manual.md | 3 ++- internal/pkg/checker/checker.go | 2 +- internal/pkg/parser/parser.go | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index bb2c306..b8f24bf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,7 +41,7 @@ linters: - staticcheck # enable before push - gocyclo # - dupl # - it's very slow, enable if you really know why you need it -# - gocognit + - gocognit - prealloc - gochecknoinits # - wsl diff --git a/docs/manual.md b/docs/manual.md index 6bc503a..cf4669e 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -159,7 +159,8 @@ IP адрес или имя хоста, где запущена БД Перечисление входных и выходных параметров сигнатуры функции или хранимой процедуры БД. В тегах у каждого поля возможно указать следующие опции: - `input` - обязательный для всех входных параметров в сигнатуре вызова; -- `output` - обязательный для всех выходных параметров в сигнатуре; Для БД поддерживающих inout параметры возможно комбинировать с `input` +- `output` - обязательный для всех выходных параметров в сигнатуре; Для БД поддерживающих inout параметры возможно комбинировать с `input`. Формат: `output[:orderNum]`. +Где [orderNum] - необязательный порядковый номер параметра, по умолчанию параметры следуют в порядке перечисления - `serializer` - позволяет навесить дополнительную сериализацию на поле; Формат: `Name[,params]`. Параметры необязательные, но если их указать то они будут переданы в функции `marshal`, `unmarshal` - `size` - длина поля в байтах для возможности валидации (в реализации для octopus не используется) diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index 6720cd6..c974a2a 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -67,7 +67,7 @@ func checkNamespace(ns *ds.NamespaceDeclaration) error { // - есть первичный ключ // - имена сущностей на которые ссылаемся на могут пересекаться с именами полей // -//nolint:gocyclo +//nolint:gocognit,gocyclo func checkFields(cl *ds.RecordPackage) error { if len(cl.Fields) > 0 && len(cl.ProcOutFields) > 0 { return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckFieldsManyDecl} diff --git a/internal/pkg/parser/parser.go b/internal/pkg/parser/parser.go index b923652..accfdfa 100644 --- a/internal/pkg/parser/parser.go +++ b/internal/pkg/parser/parser.go @@ -198,6 +198,8 @@ func parseGen(dst *ds.RecordPackage, genD *ast.GenDecl) error { } // parseDoc парсинг описания сервеной конфигурации в модели +// +//nolint:gocognit,gocyclo func parseDoc(dst *ds.RecordPackage, nodeName string, doc *ast.CommentGroup) error { if doc == nil { return arerror.ErrParseDocEmptyBoxDeclaration From b55a06f351012c5024c93f7373b0524c46b2b97a Mon Sep 17 00:00:00 2001 From: ebirukov <15173395+ebirukov@users.noreply.github.com> Date: Tue, 2 May 2023 09:41:16 +0300 Subject: [PATCH 20/20] fix gocognit linter warn --- internal/pkg/checker/checker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/pkg/checker/checker.go b/internal/pkg/checker/checker.go index c974a2a..d795fd1 100644 --- a/internal/pkg/checker/checker.go +++ b/internal/pkg/checker/checker.go @@ -206,6 +206,7 @@ func Check(files map[string]*ds.RecordPackage, linkedObjects map[string]string) return nil } +//nolint:gocognit,gocyclo func checkOctopus(cl *ds.RecordPackage) error { if cl.Server.Host == "" && cl.Server.Conf == "" { return &arerror.ErrCheckPackageDecl{Pkg: cl.Namespace.PackageName, Err: arerror.ErrCheckServerEmpty}