first commit
This commit is contained in:
681
internal/storage/trigger.go
Normal file
681
internal/storage/trigger.go
Normal file
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright 2026 Safronov Grigorii
|
||||
*
|
||||
* Licensed under the CDDL, Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
* https://opensource.org/licenses/CDDL-1.0
|
||||
*/
|
||||
|
||||
// Файл: internal/storage/trigger.go
|
||||
// Назначение: Реализация триггеров, похожих на MongoDB trigger syntax.
|
||||
// Поддерживает события: INSERT, UPDATE, DELETE, REPLACE.
|
||||
// Триггеры могут выполняться до или после события.
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"futriis/internal/log"
|
||||
)
|
||||
|
||||
// TriggerEvent определяет тип события для триггера
|
||||
type TriggerEvent string
|
||||
|
||||
const (
|
||||
TriggerBeforeInsert TriggerEvent = "BEFORE_INSERT"
|
||||
TriggerAfterInsert TriggerEvent = "AFTER_INSERT"
|
||||
TriggerBeforeUpdate TriggerEvent = "BEFORE_UPDATE"
|
||||
TriggerAfterUpdate TriggerEvent = "AFTER_UPDATE"
|
||||
TriggerBeforeDelete TriggerEvent = "BEFORE_DELETE"
|
||||
TriggerAfterDelete TriggerEvent = "AFTER_DELETE"
|
||||
TriggerBeforeReplace TriggerEvent = "BEFORE_REPLACE"
|
||||
TriggerAfterReplace TriggerEvent = "AFTER_REPLACE"
|
||||
)
|
||||
|
||||
// TriggerAction определяет действие триггера
|
||||
type TriggerAction string
|
||||
|
||||
const (
|
||||
ActionAbort TriggerAction = "abort" // Прервать операцию
|
||||
ActionSkip TriggerAction = "skip" // Пропустить операцию
|
||||
ActionModify TriggerAction = "modify" // Модифицировать документ
|
||||
ActionLog TriggerAction = "log" // Записать в лог
|
||||
ActionNotify TriggerAction = "notify" // Отправить уведомление
|
||||
ActionCustom TriggerAction = "custom" // Пользовательское действие
|
||||
)
|
||||
|
||||
// Trigger представляет триггер на коллекции
|
||||
type Trigger struct {
|
||||
Name string `msgpack:"name"`
|
||||
Collection string `msgpack:"collection"`
|
||||
Event TriggerEvent `msgpack:"event"`
|
||||
Action TriggerAction `msgpack:"action"`
|
||||
Condition *TriggerCondition `msgpack:"condition"`
|
||||
Operations []TriggerOperation `msgpack:"operations"`
|
||||
CreatedAt int64 `msgpack:"created_at"`
|
||||
UpdatedAt int64 `msgpack:"updated_at"`
|
||||
Enabled bool `msgpack:"enabled"`
|
||||
Description string `msgpack:"description"`
|
||||
mu sync.RWMutex `msgpack:"-"`
|
||||
}
|
||||
|
||||
// TriggerCondition определяет условие выполнения триггера
|
||||
type TriggerCondition struct {
|
||||
Field string `msgpack:"field"` // Поле для проверки
|
||||
Operator string `msgpack:"operator"` // Оператор: eq, ne, gt, lt, gte, lte, in, nin, exists, regex
|
||||
Value interface{} `msgpack:"value"` // Значение для сравнения
|
||||
Match string `msgpack:"match"` // Паттерн для regex
|
||||
}
|
||||
|
||||
// TriggerOperation определяет операцию, выполняемую триггером
|
||||
type TriggerOperation struct {
|
||||
Type string `msgpack:"type"` // set, unset, inc, mul, rename, currentDate
|
||||
Field string `msgpack:"field"` // Поле для операции
|
||||
Value interface{} `msgpack:"value"` // Значение для операции
|
||||
Params map[string]interface{} `msgpack:"params"`
|
||||
}
|
||||
|
||||
// TriggerExecution содержит контекст выполнения триггера
|
||||
type TriggerExecution struct {
|
||||
TriggerName string
|
||||
Event TriggerEvent
|
||||
Collection string
|
||||
Database string
|
||||
DocumentID string
|
||||
OldDocument *Document
|
||||
NewDocument *Document
|
||||
Operation string
|
||||
Timestamp time.Time
|
||||
User string
|
||||
Role string
|
||||
CustomData map[string]interface{}
|
||||
}
|
||||
|
||||
// TriggerManager управляет триггерами в СУБД
|
||||
type TriggerManager struct {
|
||||
triggers sync.Map // map[string]*Trigger (ключ: collection|event|name)
|
||||
logger *log.Logger
|
||||
mu sync.RWMutex
|
||||
auditLog []*TriggerExecution
|
||||
maxLogSize int
|
||||
}
|
||||
|
||||
var (
|
||||
globalTriggerManager *TriggerManager
|
||||
triggerManagerOnce sync.Once
|
||||
)
|
||||
|
||||
// GetTriggerManager возвращает глобальный менеджер триггеров
|
||||
func GetTriggerManager() *TriggerManager {
|
||||
triggerManagerOnce.Do(func() {
|
||||
globalTriggerManager = &TriggerManager{
|
||||
maxLogSize: 10000,
|
||||
auditLog: make([]*TriggerExecution, 0),
|
||||
}
|
||||
})
|
||||
return globalTriggerManager
|
||||
}
|
||||
|
||||
// InitTriggerManager инициализирует менеджер триггеров с логгером
|
||||
func InitTriggerManager(logger *log.Logger) {
|
||||
tm := GetTriggerManager()
|
||||
tm.logger = logger
|
||||
if logger != nil {
|
||||
logger.Info("Trigger manager initialized")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTrigger создаёт новый триггер (синтаксис MongoDB-like)
|
||||
// Пример: db.collection.createTrigger("triggerName", "BEFORE_INSERT", {
|
||||
// condition: { field: "status", operator: "eq", value: "active" },
|
||||
// action: "modify",
|
||||
// operations: [
|
||||
// { type: "set", field: "updated_at", value: "$$NOW" }
|
||||
// ]
|
||||
// })
|
||||
func (tm *TriggerManager) CreateTrigger(database, collection, name string, event TriggerEvent, config map[string]interface{}) error {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
|
||||
key := tm.getTriggerKey(collection, event, name)
|
||||
if _, exists := tm.triggers.Load(key); exists {
|
||||
return fmt.Errorf("trigger '%s' already exists on %s for event %s", name, collection, event)
|
||||
}
|
||||
|
||||
trigger := &Trigger{
|
||||
Name: name,
|
||||
Collection: collection,
|
||||
Event: event,
|
||||
Enabled: true,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
Operations: make([]TriggerOperation, 0),
|
||||
}
|
||||
|
||||
// Парсим конфигурацию триггера
|
||||
if action, ok := config["action"].(string); ok {
|
||||
switch strings.ToLower(action) {
|
||||
case "abort":
|
||||
trigger.Action = ActionAbort
|
||||
case "skip":
|
||||
trigger.Action = ActionSkip
|
||||
case "modify":
|
||||
trigger.Action = ActionModify
|
||||
case "log":
|
||||
trigger.Action = ActionLog
|
||||
case "notify":
|
||||
trigger.Action = ActionNotify
|
||||
default:
|
||||
trigger.Action = ActionCustom
|
||||
}
|
||||
}
|
||||
|
||||
// Парсим условие
|
||||
if cond, ok := config["condition"].(map[string]interface{}); ok {
|
||||
trigger.Condition = &TriggerCondition{}
|
||||
if field, ok := cond["field"].(string); ok {
|
||||
trigger.Condition.Field = field
|
||||
}
|
||||
if operator, ok := cond["operator"].(string); ok {
|
||||
trigger.Condition.Operator = operator
|
||||
}
|
||||
if value, ok := cond["value"]; ok {
|
||||
trigger.Condition.Value = value
|
||||
}
|
||||
if match, ok := cond["match"].(string); ok {
|
||||
trigger.Condition.Match = match
|
||||
}
|
||||
}
|
||||
|
||||
// Парсим операции
|
||||
if ops, ok := config["operations"].([]interface{}); ok {
|
||||
for _, opRaw := range ops {
|
||||
opMap, ok := opRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
operation := TriggerOperation{}
|
||||
if opType, ok := opMap["type"].(string); ok {
|
||||
operation.Type = opType
|
||||
}
|
||||
if field, ok := opMap["field"].(string); ok {
|
||||
operation.Field = field
|
||||
}
|
||||
if value, ok := opMap["value"]; ok {
|
||||
operation.Value = value
|
||||
}
|
||||
if params, ok := opMap["params"].(map[string]interface{}); ok {
|
||||
operation.Params = params
|
||||
}
|
||||
trigger.Operations = append(trigger.Operations, operation)
|
||||
}
|
||||
}
|
||||
|
||||
if desc, ok := config["description"].(string); ok {
|
||||
trigger.Description = desc
|
||||
}
|
||||
|
||||
tm.triggers.Store(key, trigger)
|
||||
|
||||
if tm.logger != nil {
|
||||
tm.logger.Info(fmt.Sprintf("Trigger '%s' created on %s.%s for event %s", name, database, collection, event))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DropTrigger удаляет триггер
|
||||
func (tm *TriggerManager) DropTrigger(collection, event, name string) error {
|
||||
key := tm.getTriggerKey(collection, TriggerEvent(event), name)
|
||||
if _, exists := tm.triggers.LoadAndDelete(key); !exists {
|
||||
return fmt.Errorf("trigger '%s' not found on %s for event %s", name, collection, event)
|
||||
}
|
||||
|
||||
if tm.logger != nil {
|
||||
tm.logger.Info(fmt.Sprintf("Trigger '%s' dropped from %s for event %s", name, collection, event))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTrigger возвращает триггер по имени
|
||||
func (tm *TriggerManager) GetTrigger(collection, event, name string) (*Trigger, error) {
|
||||
key := tm.getTriggerKey(collection, TriggerEvent(event), name)
|
||||
if val, ok := tm.triggers.Load(key); ok {
|
||||
return val.(*Trigger), nil
|
||||
}
|
||||
return nil, fmt.Errorf("trigger not found: %s", name)
|
||||
}
|
||||
|
||||
// ListTriggers возвращает список всех триггеров для коллекции
|
||||
func (tm *TriggerManager) ListTriggers(collection string) []*Trigger {
|
||||
triggers := make([]*Trigger, 0)
|
||||
tm.triggers.Range(func(key, value interface{}) bool {
|
||||
trigger := value.(*Trigger)
|
||||
if collection == "" || trigger.Collection == collection {
|
||||
triggers = append(triggers, trigger)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return triggers
|
||||
}
|
||||
|
||||
// ListTriggersByEvent возвращает триггеры для конкретного события
|
||||
func (tm *TriggerManager) ListTriggersByEvent(collection string, event TriggerEvent) []*Trigger {
|
||||
triggers := make([]*Trigger, 0)
|
||||
tm.triggers.Range(func(key, value interface{}) bool {
|
||||
trigger := value.(*Trigger)
|
||||
if trigger.Collection == collection && trigger.Event == event && trigger.Enabled {
|
||||
triggers = append(triggers, trigger)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return triggers
|
||||
}
|
||||
|
||||
// EnableTrigger включает триггер
|
||||
func (tm *TriggerManager) EnableTrigger(collection, event, name string) error {
|
||||
key := tm.getTriggerKey(collection, TriggerEvent(event), name)
|
||||
val, ok := tm.triggers.Load(key)
|
||||
if !ok {
|
||||
return fmt.Errorf("trigger not found: %s", name)
|
||||
}
|
||||
trigger := val.(*Trigger)
|
||||
trigger.mu.Lock()
|
||||
trigger.Enabled = true
|
||||
trigger.UpdatedAt = time.Now().UnixMilli()
|
||||
trigger.mu.Unlock()
|
||||
tm.triggers.Store(key, trigger)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableTrigger выключает триггер
|
||||
func (tm *TriggerManager) DisableTrigger(collection, event, name string) error {
|
||||
key := tm.getTriggerKey(collection, TriggerEvent(event), name)
|
||||
val, ok := tm.triggers.Load(key)
|
||||
if !ok {
|
||||
return fmt.Errorf("trigger not found: %s", name)
|
||||
}
|
||||
trigger := val.(*Trigger)
|
||||
trigger.mu.Lock()
|
||||
trigger.Enabled = false
|
||||
trigger.UpdatedAt = time.Now().UnixMilli()
|
||||
trigger.mu.Unlock()
|
||||
tm.triggers.Store(key, trigger)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteTriggers выполняет все триггеры для данного события
|
||||
// Возвращает: modifiedDocument, shouldAbort, error
|
||||
func (tm *TriggerManager) ExecuteTriggers(execCtx *TriggerExecution) (*Document, bool, error) {
|
||||
triggers := tm.ListTriggersByEvent(execCtx.Collection, execCtx.Event)
|
||||
|
||||
if len(triggers) == 0 {
|
||||
return execCtx.NewDocument, false, nil
|
||||
}
|
||||
|
||||
currentDoc := execCtx.NewDocument
|
||||
if currentDoc == nil && execCtx.OldDocument != nil {
|
||||
currentDoc = execCtx.OldDocument.Clone()
|
||||
}
|
||||
|
||||
for _, trigger := range triggers {
|
||||
if !trigger.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
// Проверяем условие
|
||||
if trigger.Condition != nil {
|
||||
if !tm.evaluateCondition(execCtx, trigger.Condition) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Выполняем действие триггера
|
||||
switch trigger.Action {
|
||||
case ActionAbort:
|
||||
tm.logExecution(execCtx, trigger, "aborted")
|
||||
return currentDoc, true, fmt.Errorf("operation aborted by trigger: %s", trigger.Name)
|
||||
|
||||
case ActionSkip:
|
||||
tm.logExecution(execCtx, trigger, "skipped")
|
||||
return currentDoc, true, nil
|
||||
|
||||
case ActionModify:
|
||||
if currentDoc != nil {
|
||||
currentDoc = tm.applyOperations(currentDoc, trigger.Operations, execCtx)
|
||||
}
|
||||
tm.logExecution(execCtx, trigger, "modified")
|
||||
|
||||
case ActionLog:
|
||||
tm.logExecution(execCtx, trigger, "logged")
|
||||
if tm.logger != nil {
|
||||
tm.logger.Info(fmt.Sprintf("Trigger %s executed on %s.%s (event: %s, doc: %s)",
|
||||
trigger.Name, execCtx.Database, execCtx.Collection, execCtx.Event, execCtx.DocumentID))
|
||||
}
|
||||
|
||||
case ActionNotify:
|
||||
tm.logExecution(execCtx, trigger, "notified")
|
||||
// Здесь можно отправить уведомление через WebSocket или другой канал
|
||||
}
|
||||
}
|
||||
|
||||
return currentDoc, false, nil
|
||||
}
|
||||
|
||||
// evaluateCondition проверяет условие триггера
|
||||
func (tm *TriggerManager) evaluateCondition(execCtx *TriggerExecution, cond *TriggerCondition) bool {
|
||||
var docToCheck *Document
|
||||
if execCtx.NewDocument != nil {
|
||||
docToCheck = execCtx.NewDocument
|
||||
} else if execCtx.OldDocument != nil {
|
||||
docToCheck = execCtx.OldDocument
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
fieldValue, err := docToCheck.GetField(cond.Field)
|
||||
if err != nil {
|
||||
// Поле не существует
|
||||
if cond.Operator == "exists" {
|
||||
if existsVal, ok := cond.Value.(bool); ok && !existsVal {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
switch cond.Operator {
|
||||
case "eq":
|
||||
return fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", cond.Value)
|
||||
case "ne":
|
||||
return fmt.Sprintf("%v", fieldValue) != fmt.Sprintf("%v", cond.Value)
|
||||
case "gt":
|
||||
return compareNumbers(fieldValue, cond.Value) > 0
|
||||
case "lt":
|
||||
return compareNumbers(fieldValue, cond.Value) < 0
|
||||
case "gte":
|
||||
return compareNumbers(fieldValue, cond.Value) >= 0
|
||||
case "lte":
|
||||
return compareNumbers(fieldValue, cond.Value) <= 0
|
||||
case "in":
|
||||
if arr, ok := cond.Value.([]interface{}); ok {
|
||||
for _, v := range arr {
|
||||
if fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
case "nin":
|
||||
if arr, ok := cond.Value.([]interface{}); ok {
|
||||
for _, v := range arr {
|
||||
if fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
case "exists":
|
||||
if existsVal, ok := cond.Value.(bool); ok {
|
||||
return existsVal
|
||||
}
|
||||
return true
|
||||
case "regex":
|
||||
if pattern, ok := cond.Value.(string); ok {
|
||||
matched, _ := regexp.MatchString(pattern, fmt.Sprintf("%v", fieldValue))
|
||||
return matched
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// applyOperations применяет операции к документу
|
||||
func (tm *TriggerManager) applyOperations(doc *Document, ops []TriggerOperation, execCtx *TriggerExecution) *Document {
|
||||
if doc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := doc.Clone()
|
||||
|
||||
for _, op := range ops {
|
||||
switch op.Type {
|
||||
case "set":
|
||||
value := tm.resolveValue(op.Value, execCtx)
|
||||
result.SetField(op.Field, value)
|
||||
|
||||
case "unset":
|
||||
result.DeleteField(op.Field)
|
||||
|
||||
case "inc":
|
||||
if incVal, ok := toFloat64(op.Value); ok {
|
||||
if current, err := result.GetField(op.Field); err == nil {
|
||||
if currVal, ok := toFloat64(current); ok {
|
||||
result.SetField(op.Field, currVal+incVal)
|
||||
}
|
||||
} else {
|
||||
result.SetField(op.Field, incVal)
|
||||
}
|
||||
}
|
||||
|
||||
case "mul":
|
||||
if mulVal, ok := toFloat64(op.Value); ok {
|
||||
if current, err := result.GetField(op.Field); err == nil {
|
||||
if currVal, ok := toFloat64(current); ok {
|
||||
result.SetField(op.Field, currVal*mulVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "rename":
|
||||
if newName, ok := op.Value.(string); ok {
|
||||
if val, err := result.GetField(op.Field); err == nil {
|
||||
result.SetField(newName, val)
|
||||
result.DeleteField(op.Field)
|
||||
}
|
||||
}
|
||||
|
||||
case "currentDate":
|
||||
result.SetField(op.Field, time.Now().UnixMilli())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// resolveValue разрешает специальные значения типа $$NOW, $$USER
|
||||
func (tm *TriggerManager) resolveValue(value interface{}, execCtx *TriggerExecution) interface{} {
|
||||
if strVal, ok := value.(string); ok {
|
||||
switch strVal {
|
||||
case "$$NOW":
|
||||
return time.Now().UnixMilli()
|
||||
case "$$USER":
|
||||
if execCtx.User != "" {
|
||||
return execCtx.User
|
||||
}
|
||||
return "anonymous"
|
||||
case "$$ROLE":
|
||||
if execCtx.Role != "" {
|
||||
return execCtx.Role
|
||||
}
|
||||
return "anonymous"
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// logExecution логирует выполнение триггера
|
||||
func (tm *TriggerManager) logExecution(execCtx *TriggerExecution, trigger *Trigger, result string) {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
|
||||
execCtx.TriggerName = trigger.Name
|
||||
execCtx.Timestamp = time.Now()
|
||||
|
||||
if len(tm.auditLog) >= tm.maxLogSize {
|
||||
tm.auditLog = tm.auditLog[1:]
|
||||
}
|
||||
tm.auditLog = append(tm.auditLog, execCtx)
|
||||
}
|
||||
|
||||
// GetTriggerExecutionLog возвращает лог выполнения триггеров
|
||||
func (tm *TriggerManager) GetTriggerExecutionLog() []*TriggerExecution {
|
||||
tm.mu.RLock()
|
||||
defer tm.mu.RUnlock()
|
||||
|
||||
result := make([]*TriggerExecution, len(tm.auditLog))
|
||||
copy(result, tm.auditLog)
|
||||
return result
|
||||
}
|
||||
|
||||
// getTriggerKey возвращает ключ для хранения триггера
|
||||
func (tm *TriggerManager) getTriggerKey(collection string, event TriggerEvent, name string) string {
|
||||
return fmt.Sprintf("%s|%s|%s", collection, event, name)
|
||||
}
|
||||
|
||||
// compareNumbers сравнивает два числа
|
||||
func compareNumbers(a, b interface{}) int {
|
||||
aVal, aOk := toFloat64(a)
|
||||
bVal, bOk := toFloat64(b)
|
||||
|
||||
if aOk && bOk {
|
||||
if aVal < bVal {
|
||||
return -1
|
||||
}
|
||||
if aVal > bVal {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// MongoDBLikeTriggerConfig создаёт конфигурацию триггера в стиле MongoDB
|
||||
// Пример использования:
|
||||
// config := MongoDBLikeTriggerConfig().
|
||||
// On("BEFORE_INSERT").
|
||||
// Condition("status", "eq", "active").
|
||||
// Set("updated_at", "$$NOW").
|
||||
// Build()
|
||||
func MongoDBLikeTriggerConfig() *TriggerConfigBuilder {
|
||||
return &TriggerConfigBuilder{
|
||||
config: make(map[string]interface{}),
|
||||
ops: make([]interface{}, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerConfigBuilder строитель конфигурации триггера
|
||||
type TriggerConfigBuilder struct {
|
||||
config map[string]interface{}
|
||||
ops []interface{}
|
||||
}
|
||||
|
||||
// On устанавливает событие триггера
|
||||
func (b *TriggerConfigBuilder) On(event string) *TriggerConfigBuilder {
|
||||
b.config["event"] = event
|
||||
return b
|
||||
}
|
||||
|
||||
// Condition добавляет условие
|
||||
func (b *TriggerConfigBuilder) Condition(field, operator string, value interface{}) *TriggerConfigBuilder {
|
||||
b.config["condition"] = map[string]interface{}{
|
||||
"field": field,
|
||||
"operator": operator,
|
||||
"value": value,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ConditionRegex добавляет regex условие
|
||||
func (b *TriggerConfigBuilder) ConditionRegex(field, pattern string) *TriggerConfigBuilder {
|
||||
b.config["condition"] = map[string]interface{}{
|
||||
"field": field,
|
||||
"operator": "regex",
|
||||
"value": pattern,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Set добавляет операцию установки поля
|
||||
func (b *TriggerConfigBuilder) Set(field string, value interface{}) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "set",
|
||||
"field": field,
|
||||
"value": value,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// Unset добавляет операцию удаления поля
|
||||
func (b *TriggerConfigBuilder) Unset(field string) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "unset",
|
||||
"field": field,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// Inc добавляет операцию инкремента
|
||||
func (b *TriggerConfigBuilder) Inc(field string, value float64) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "inc",
|
||||
"field": field,
|
||||
"value": value,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// Mul добавляет операцию умножения
|
||||
func (b *TriggerConfigBuilder) Mul(field string, value float64) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "mul",
|
||||
"field": field,
|
||||
"value": value,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// Rename добавляет операцию переименования поля
|
||||
func (b *TriggerConfigBuilder) Rename(oldName, newName string) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "rename",
|
||||
"field": oldName,
|
||||
"value": newName,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// CurrentDate добавляет операцию установки текущей даты
|
||||
func (b *TriggerConfigBuilder) CurrentDate(field string) *TriggerConfigBuilder {
|
||||
b.ops = append(b.ops, map[string]interface{}{
|
||||
"type": "currentDate",
|
||||
"field": field,
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// Action устанавливает действие триггера
|
||||
func (b *TriggerConfigBuilder) Action(action string) *TriggerConfigBuilder {
|
||||
b.config["action"] = action
|
||||
return b
|
||||
}
|
||||
|
||||
// Description устанавливает описание триггера
|
||||
func (b *TriggerConfigBuilder) Description(desc string) *TriggerConfigBuilder {
|
||||
b.config["description"] = desc
|
||||
return b
|
||||
}
|
||||
|
||||
// Build собирает конфигурацию
|
||||
func (b *TriggerConfigBuilder) Build() map[string]interface{} {
|
||||
b.config["operations"] = b.ops
|
||||
return b.config
|
||||
}
|
||||
Reference in New Issue
Block a user