Select Git revision
service.go 6.05 KiB
package items
import (
"context"
"regexp"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/filter"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"git.perx.ru/perxis/perxis-go/pkg/schema/field"
)
// @microgen grpc
// @protobuf git.perx.ru/perxis/perxis-go/proto/items
// @grpc-addr content.items.Items
type Items interface {
Create(ctx context.Context, item *Item, opts ...*CreateOptions) (created *Item, err error)
Introspect(ctx context.Context, item *Item, opts ...*IntrospectOptions) (itm *Item, sch *schema.Schema, err error)
Get(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*GetOptions) (item *Item, err error)
Find(ctx context.Context, spaceId, envId, collectionId string, filter *Filter, options ...*FindOptions) (items []*Item, total int, err error)
Update(ctx context.Context, item *Item, options ...*UpdateOptions) (err error)
// Delete выполняет удаление элемента
// Если установлен флаг DeleteOptions.Erase то данные будут полностью удалены из системы.
// В противном случае выполняется "мягкое удаление", элемент помечается как удаленный и может быть восстановлен с помощью метода Items.Undelete и получен в Items.Get/Find
Delete(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*DeleteOptions) (err error)
// Undelete восстанавливает элементы после "мягкого удаление"
Undelete(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*UndeleteOptions) (err error)
Publish(ctx context.Context, item *Item, options ...*PublishOptions) (err error)
Unpublish(ctx context.Context, item *Item, options ...*UnpublishOptions) (err error)
GetPublished(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*GetPublishedOptions) (item *Item, err error)
FindPublished(ctx context.Context, spaceId, envId, collectionId string, filter *Filter, options ...*FindPublishedOptions) (items []*Item, total int, err error)
GetRevision(ctx context.Context, spaceId, envId, collectionId, itemId, revisionId string, options ...*GetRevisionOptions) (item *Item, err error)
ListRevisions(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*ListRevisionsOptions) (items []*Item, err error)
Archive(ctx context.Context, item *Item, options ...*ArchiveOptions) (err error)
FindArchived(ctx context.Context, spaceId, envId, collectionId string, filter *Filter, options ...*FindArchivedOptions) (items []*Item, total int, err error)
Unarchive(ctx context.Context, item *Item, options ...*UnarchiveOptions) (err error)
// Aggregate выполняет агрегацию данных
Aggregate(ctx context.Context, spaceId, envId, collectionId string, filter *Filter, options ...*AggregateOptions) (result map[string]interface{}, err error)
// AggregatePublished выполняет агрегацию опубликованных данных
AggregatePublished(ctx context.Context, spaceId, envId, collectionId string, filter *Filter, options ...*AggregatePublishedOptions) (result map[string]interface{}, err error)
}
// PreSaver - интерфейс, который может быть реализован полем, чтобы получать событие PreSave перед сохранением Item в Storage
type PreSaver interface {
PreSave(ctx context.Context, f *field.Field, v interface{}, itemCtx *Context) (interface{}, bool, error)
}
type Filter struct {
ID []string
Data []*filter.Filter
Search string // Поиск, одновременно поддерживается только один запрос
Q []string
}
func NewFilter(params ...interface{}) *Filter {
f := &Filter{}
for _, p := range params {
switch v := p.(type) {
case *filter.Filter:
f.Data = append(f.Data, v)
case string:
f.Q = append(f.Q, v)
}
}
return f
}
// AggregateExpRe - формат, которому должна соответствовать формула расчета данных
var AggregateExpRe = regexp.MustCompile(`([a-zA-Z]+)\((.*)\)`)
func ParseAggregateExp(exp string) (string, string, bool) {
ss := AggregateExpRe.FindAllStringSubmatch(exp, -1)
if len(ss) == 0 || len(ss[0]) < 2 {
return "", "", false
}
return ss[0][1], ss[0][2], true
}
func DecodeAggregateResult(ctx context.Context, request map[string]string, r map[string]interface{}, s *schema.Schema) (map[string]interface{}, error) {
result := make(map[string]interface{}, len(r))
for outputField, exp := range request {
funcName, fldName, ok := ParseAggregateExp(exp)
if !ok || fldName == "" {
if v, ok := r[outputField]; ok {
result[outputField] = v
}
continue
}
schemaFld := s.GetField(fldName)
if schemaFld == nil {
if v, ok := r[outputField]; ok {
result[outputField] = v
}
continue
}
if funcName == "distinct" {
schemaFld = field.Array(schemaFld)
}
data, err := schema.Decode(ctx, schemaFld, r[outputField])
if err != nil {
return nil, errors.Wrapf(err, "decode data for field '%s'", outputField)
}
result[outputField] = data
}
return result, nil
}
func EncodeAggregateResult(ctx context.Context, request map[string]string, r map[string]interface{}, s *schema.Schema) (map[string]interface{}, error) {
result := make(map[string]interface{}, len(r))
for outputField, exp := range request {
funcName, fldName, ok := ParseAggregateExp(exp)
if !ok || fldName == "" {
if v, ok := r[outputField]; ok {
result[outputField] = v
}
continue
}
schemaFld := s.GetField(fldName)
if schemaFld == nil {
if v, ok := r[outputField]; ok {
result[outputField] = v
}
continue
}
if funcName == "distinct" {
schemaFld = field.Array(schemaFld)
}
data, err := schema.Encode(ctx, schemaFld, r[outputField])
if err != nil {
return nil, errors.Wrapf(err, "decode data for field '%s'", outputField)
}
result[outputField] = data
}
return result, nil
}