package collections import ( "time" "git.perx.ru/perxis/perxis-go/pkg/data" "git.perx.ru/perxis/perxis-go/pkg/permission" "git.perx.ru/perxis/perxis-go/pkg/schema" ) // Config type Config struct { SourceSpaceID string SourceEnvID string SourceCollectionID string SourceSchema *schema.Schema } // Access - описывает текущие ограничения на доступ к элементам коллекции для текущего // пользователя type Access struct { Actions []permission.Action // Список разрешенных действия с элементами коллекции HiddenFields []string // Поля не отображаемые в интерфейсе и не возвращаемые API DenyReadFields []string // Поля недоступные для редактирования и не обновляемые через API DenyWriteFields []string // Поля отображаемые в интерфейсе, но не возвращаемые в API ReadFilter string // Дополнительный фильтр на чтение WriteFilter string // Дополнительный фильтр на запись Hidden bool // Скрыть коллекцию в интерфейсе от пользователя } func (a Access) Clone() *Access { clone := &Access{ Actions: make([]permission.Action, len(a.Actions)), HiddenFields: make([]string, len(a.HiddenFields)), DenyReadFields: make([]string, len(a.DenyReadFields)), DenyWriteFields: make([]string, len(a.DenyWriteFields)), Hidden: a.Hidden, } copy(clone.Actions, a.Actions) copy(clone.HiddenFields, a.HiddenFields) copy(clone.DenyReadFields, a.DenyReadFields) copy(clone.DenyWriteFields, a.DenyWriteFields) return clone } func (a Access) Can(action permission.Action) bool { for _, act := range a.Actions { if act == action { return true } } return false } type Collection struct { ID string `json:"id" bson:"id"` SpaceID string `json:"spaceId" bson:"-"` EnvID string `json:"envId" bson:"-"` Name string `json:"name" bson:"name"` Single *bool `json:"single" bson:"single,omitempty"` // В коллекции может быть только один документ System *bool `json:"system" bson:"system,omitempty"` // Системная коллекция NoData *bool `json:"no_data" bson:"no_data"` // Коллекция не содержит элементы. Схема используется для включения в другие схемы Hidden bool `json:"hidden" bson:"hidden"` // Коллекция скрыта в административном интерфейсе NoArchive bool `json:"no_archive" bson:"no_archive,omitempty"` // Коллекция без архива NoRevisions bool `json:"no_revisions" bson:"no_revisions,omitempty"` // Не хранить историю изменений MaxRevisions uint32 `json:"max_revisions" bson:"max_revisions,omitempty"` // Максимальное количество хранимых ревизий RevisionTTL time.Duration `json:"revision_ttl" bson:"revision_ttl,omitempty"` // Время жизни ревизии // Все записи коллекции считаются опубликованными, функции публикации и снятия с публикации недоступны. // При включении параметра коллекции "без публикации" все записи, независимо от статуса, будут считаться опубликованными. // При отключении параметра "без публикации" статусы публикации будут восстановлены. NoPublish bool `json:"no_publish" bson:"no_publish,omitempty"` Schema *schema.Schema `json:"schema" bson:"schema"` Access *Access `json:"access" bson:"-"` // Ограничения на доступ к элементам коллекции. Отсутствие объекта означает неограниченный доступ // StateInfo отображает состояние коллекции: // - State: идентификатор состояния коллекции (new/preparing/ready/error/changed) // - Info: дополнительная информация о состоянии коллекции (например, если при // применении схемы к коллекции произошла ошибка) // - StartedAt: время, в которое коллекция перешла в состояние `Preparing` StateInfo *StateInfo `json:"state_info" bson:"state_info,omitempty"` // todo: показывать в интерфейсе как readonly // View - Если значение поля непустое, то коллекция является View ("отображением" // части данных другой коллекции согласно View.Filter) View *View `json:"view,omitempty" bson:"view,omitempty"` // Tags - список тегов коллекции. Добавляются при отправке событий events Tags []string `json:"tags,omitempty" bson:"tags,omitempty"` Config *Config `json:"-" bson:"-"` } // GetID возвращает идентификатор коллекции func (c Collection) GetID() string { return c.ID } func (c Collection) GetSpaceID() string { return c.SpaceID } // Equal сравнивает две коллекции, за исключением Schema, Access, StateInfo и Config func (c Collection) Equal(other *Collection) bool { if c.ID != other.ID || c.SpaceID != other.SpaceID || c.EnvID != other.EnvID || c.Name != other.Name || c.IsNoData() != other.IsNoData() || c.IsSingle() != other.IsSingle() || c.IsSystem() != other.IsSystem() || c.Hidden != other.Hidden || c.NoPublish != other.NoPublish || c.NoArchive != other.NoArchive || c.NoRevisions != other.NoRevisions || c.MaxRevisions != other.MaxRevisions || c.RevisionTTL != other.RevisionTTL || !c.View.Equal(other.View) || !data.ElementsMatch(c.Tags, other.Tags) { return false } return true } type View struct { SpaceID string `json:"space_id" bson:"space_id"` // SpaceID оригинальной коллекции EnvID string `json:"environment_id" bson:"environment_id"` // EnvID оригинальной коллекции CollectionID string `json:"collection_id" bson:"collection_id"` // CollectionID оригинальной коллекции Filter string `json:"filter" bson:"filter,omitempty"` // Правила фильтрации записей оригинальной коллекции } func (v *View) Equal(v1 *View) bool { return v == v1 || v != nil && v1 != nil && v.SpaceID == v1.SpaceID && v.EnvID == v1.EnvID && v.CollectionID == v1.CollectionID && v.Filter == v1.Filter } type StateInfo struct { State State `json:"state" bson:"state"` Info string `json:"info" bson:"info"` StartedAt time.Time `json:"started_at,omitempty" bson:"started_at,omitempty"` DBVersion uint32 `json:"db_version" bson:"db_version"` } type State int func (s State) String() string { var state string switch s { case StateNew: state = "New" case StatePreparing: state = "Preparing" case StateReady: state = "Ready" case StateError: state = "Error" case StateChanged: state = "Changed" default: state = "Unknown" } return state } const ( StateNew State = iota StatePreparing StateReady StateError StateChanged ) func (c Collection) Clone() *Collection { clone := &Collection{ ID: c.ID, SpaceID: c.SpaceID, EnvID: c.EnvID, Name: c.Name, NoData: c.NoData, Hidden: c.Hidden, NoPublish: c.NoPublish, NoArchive: c.NoArchive, NoRevisions: c.NoRevisions, MaxRevisions: c.MaxRevisions, RevisionTTL: c.RevisionTTL, } if c.Single != nil { single := *c.Single clone.Single = &single } if c.System != nil { system := *c.System clone.System = &system } if c.Schema != nil { clone.Schema = c.Schema.Clone(false) } if c.Access != nil { clone.Access = c.Access.Clone() } if c.StateInfo != nil { info := *c.StateInfo clone.StateInfo = &info } if c.View != nil { view := *c.View clone.View = &view } if c.Config != nil { cfg := *c.Config clone.Config = &cfg } if c.Tags != nil { clone.Tags = append([]string{}, c.Tags...) } return clone } func (c Collection) IsSingle() bool { return c.Single != nil && *c.Single } func (c Collection) IsNoData() bool { return c.NoData != nil && *c.NoData } func (c Collection) IsSystem() bool { return c.System != nil && *c.System } func (c Collection) IsView() bool { return c.View != nil } func GetCollectionsIDs(collections []*Collection) []string { res := make([]string, len(collections)) for i, c := range collections { res[i] = c.ID } return res } func AccessFromRule(rule *permission.Rule) *Access { if rule == nil { return nil } return &Access{ Actions: rule.Actions, HiddenFields: rule.HiddenFields, DenyReadFields: rule.DenyReadFields, DenyWriteFields: rule.DenyWriteFields, ReadFilter: rule.ReadFilter, WriteFilter: rule.WriteFilter, Hidden: rule.Hidden, } } func RuleFromAccess(access *Access) *permission.Rule { if access == nil { return nil } return &permission.Rule{ Actions: access.Actions, HiddenFields: access.HiddenFields, DenyReadFields: access.DenyReadFields, DenyWriteFields: access.DenyWriteFields, ReadFilter: access.ReadFilter, WriteFilter: access.WriteFilter, Hidden: access.Hidden, } }