snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
588
server/channels/app/properties/hooks.go
Normal file
588
server/channels/app/properties/hooks.go
Normal file
@@ -0,0 +1,588 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package properties
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
)
|
||||
|
||||
// errNilHookResult is returned when a pre-hook returns a nil result without an
|
||||
// error. This catches buggy hook implementations early rather than letting a
|
||||
// nil propagate into the store layer.
|
||||
var (
|
||||
errNilHookResult = errors.New("property hook returned nil result")
|
||||
errFieldCardinalityBroken = errors.New("PostGetPropertyFields hook returned fewer fields than it received")
|
||||
)
|
||||
|
||||
// PropertyHook defines an interface for hooks that run before and after property
|
||||
// service operations. Hooks can inspect and modify inputs (pre-hooks) or filter
|
||||
// outputs (post-hooks). A pre-hook returns an error to block the operation; a
|
||||
// post-hook returns an error to suppress the result. Returning nil means the
|
||||
// hook has no objection and the operation may proceed.
|
||||
//
|
||||
// Pre-hooks receive the operation's input parameters and may return modified
|
||||
// versions. Post-hooks receive the operation's results and may return filtered
|
||||
// or modified versions.
|
||||
//
|
||||
// Multiple hooks are called in registration order. Each hook receives the
|
||||
// output of the previous hook (or the original input for the first hook).
|
||||
type PropertyHook interface {
|
||||
// Field pre-hooks (write operations)
|
||||
|
||||
PreCreatePropertyField(rctx request.CTX, field *model.PropertyField) (*model.PropertyField, error)
|
||||
PreUpdatePropertyField(rctx request.CTX, groupID string, field *model.PropertyField) (*model.PropertyField, error)
|
||||
PreUpdatePropertyFields(rctx request.CTX, groupID string, fields []*model.PropertyField) ([]*model.PropertyField, error)
|
||||
PreDeletePropertyField(rctx request.CTX, groupID string, id string) error
|
||||
|
||||
// PostUpdatePropertyFields runs after a successful field update (including
|
||||
// the linked-field propagation pass). It receives the pre-update state of
|
||||
// the requested fields (parallel to requested), the post-update requested
|
||||
// fields, and the post-update propagated fields. Hooks may transform attrs
|
||||
// on either bucket (e.g. redact information for the caller); the
|
||||
// dispatcher enforces cardinality preservation on both buckets so a buggy
|
||||
// hook that drops fields surfaces an error rather than silently truncating
|
||||
// the broadcast. Returns the IDs of fields whose dependent property values
|
||||
// were cleared as a side effect (e.g. type-change cleanup); the caller
|
||||
// publishes the corresponding WS events. Errors are best-effort: the
|
||||
// dispatcher logs and continues, the update is not rolled back.
|
||||
PostUpdatePropertyFields(rctx request.CTX, groupID string, prev, requested, propagated []*model.PropertyField) (newRequested, newPropagated []*model.PropertyField, clearedFieldIDs []string, err error)
|
||||
|
||||
// Field pre-hook for count operations. Count operations return only a
|
||||
// scalar so there is no post-hook — access control applied to per-row
|
||||
// data does not apply, but license/group-level gating still does.
|
||||
// Return an error to block the count.
|
||||
PreCountPropertyFields(rctx request.CTX, groupID string) error
|
||||
|
||||
// Field post-hooks (read operations)
|
||||
//
|
||||
// PostGetPropertyField is called after retrieving a single field (by ID or by name).
|
||||
// Implementations must return a non-nil field; returning nil is treated as a
|
||||
// hook bug and the dispatcher surfaces errNilHookResult. To block a caller
|
||||
// from seeing a field, return a sentinel error instead.
|
||||
PostGetPropertyField(rctx request.CTX, field *model.PropertyField) (*model.PropertyField, error)
|
||||
// PostGetPropertyFields is called after retrieving multiple fields (by IDs or search).
|
||||
// Implementations must preserve slice length — the dispatcher enforces this and will
|
||||
// return an error if a hook returns fewer fields than it received.
|
||||
PostGetPropertyFields(rctx request.CTX, fields []*model.PropertyField) ([]*model.PropertyField, error)
|
||||
|
||||
// Value pre-hooks (write operations)
|
||||
|
||||
PreCreatePropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error)
|
||||
PreCreatePropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error)
|
||||
PreUpdatePropertyValue(rctx request.CTX, groupID string, value *model.PropertyValue) (*model.PropertyValue, error)
|
||||
PreUpdatePropertyValues(rctx request.CTX, groupID string, values []*model.PropertyValue) ([]*model.PropertyValue, error)
|
||||
PreUpsertPropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error)
|
||||
PreUpsertPropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error)
|
||||
PreDeletePropertyValue(rctx request.CTX, groupID string, id string) error
|
||||
PreDeletePropertyValuesForTarget(rctx request.CTX, groupID string, targetType string, targetID string) error
|
||||
PreDeletePropertyValuesForField(rctx request.CTX, groupID string, fieldID string) error
|
||||
|
||||
// Value post-hooks (write operations)
|
||||
//
|
||||
// These run after a successful store write. They are best-effort: the
|
||||
// dispatcher logs and continues on a hook error, and the write is never
|
||||
// rolled back; hooks must not mutate the values. Delete hooks receive the
|
||||
// delete selector and, when available, the value's pre-delete snapshot.
|
||||
PostCreatePropertyValue(rctx request.CTX, value *model.PropertyValue) error
|
||||
PostCreatePropertyValues(rctx request.CTX, values []*model.PropertyValue) error
|
||||
PostUpdatePropertyValue(rctx request.CTX, value *model.PropertyValue) error
|
||||
PostUpdatePropertyValues(rctx request.CTX, values []*model.PropertyValue) error
|
||||
PostUpsertPropertyValue(rctx request.CTX, value *model.PropertyValue) error
|
||||
PostUpsertPropertyValues(rctx request.CTX, values []*model.PropertyValue) error
|
||||
PostDeletePropertyValue(rctx request.CTX, groupID, valueID string, deleted *model.PropertyValue) error
|
||||
PostDeletePropertyValuesForTarget(rctx request.CTX, groupID string, targetType string, targetID string) error
|
||||
PostDeletePropertyValuesForField(rctx request.CTX, groupID string, fieldID string) error
|
||||
|
||||
// Value post-hooks (read operations)
|
||||
//
|
||||
// PostGetPropertyValue is called after retrieving a single value.
|
||||
// Return nil value to indicate the value is not accessible.
|
||||
PostGetPropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error)
|
||||
// PostGetPropertyValues is called after retrieving multiple values (by IDs or search).
|
||||
// Implementations may remove entries from the returned slice.
|
||||
PostGetPropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error)
|
||||
}
|
||||
|
||||
// BasePropertyHook provides default passthrough implementations for every
|
||||
// PropertyHook method. Embed it in concrete hooks to only override the
|
||||
// methods you care about.
|
||||
type BasePropertyHook struct{}
|
||||
|
||||
func (BasePropertyHook) PreCreatePropertyField(_ request.CTX, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
return field, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpdatePropertyField(_ request.CTX, _ string, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
return field, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpdatePropertyFields(_ request.CTX, _ string, fields []*model.PropertyField) ([]*model.PropertyField, error) {
|
||||
return fields, nil
|
||||
}
|
||||
func (BasePropertyHook) PreDeletePropertyField(_ request.CTX, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostUpdatePropertyFields(_ request.CTX, _ string, _, requested, propagated []*model.PropertyField) ([]*model.PropertyField, []*model.PropertyField, []string, error) {
|
||||
return requested, propagated, nil, nil
|
||||
}
|
||||
func (BasePropertyHook) PreCountPropertyFields(_ request.CTX, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostGetPropertyField(_ request.CTX, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
return field, nil
|
||||
}
|
||||
func (BasePropertyHook) PostGetPropertyFields(_ request.CTX, fields []*model.PropertyField) ([]*model.PropertyField, error) {
|
||||
return fields, nil
|
||||
}
|
||||
func (BasePropertyHook) PreCreatePropertyValue(_ request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
return value, nil
|
||||
}
|
||||
func (BasePropertyHook) PreCreatePropertyValues(_ request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
return values, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpdatePropertyValue(_ request.CTX, _ string, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
return value, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpdatePropertyValues(_ request.CTX, _ string, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
return values, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpsertPropertyValue(_ request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
return value, nil
|
||||
}
|
||||
func (BasePropertyHook) PreUpsertPropertyValues(_ request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
return values, nil
|
||||
}
|
||||
func (BasePropertyHook) PreDeletePropertyValue(_ request.CTX, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PreDeletePropertyValuesForTarget(_ request.CTX, _ string, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PreDeletePropertyValuesForField(_ request.CTX, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostCreatePropertyValue(_ request.CTX, _ *model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostCreatePropertyValues(_ request.CTX, _ []*model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostUpdatePropertyValue(_ request.CTX, _ *model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostUpdatePropertyValues(_ request.CTX, _ []*model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostUpsertPropertyValue(_ request.CTX, _ *model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostUpsertPropertyValues(_ request.CTX, _ []*model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostDeletePropertyValue(_ request.CTX, _, _ string, _ *model.PropertyValue) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostDeletePropertyValuesForTarget(_ request.CTX, _ string, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostDeletePropertyValuesForField(_ request.CTX, _ string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (BasePropertyHook) PostGetPropertyValue(_ request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
return value, nil
|
||||
}
|
||||
func (BasePropertyHook) PostGetPropertyValues(_ request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// AddHook registers a hook with the property service. Hooks are called in
|
||||
// registration order for each operation.
|
||||
func (ps *PropertyService) AddHook(hook PropertyHook) {
|
||||
ps.hooks = append(ps.hooks, hook)
|
||||
}
|
||||
|
||||
// runPreCreatePropertyField runs all registered pre-hooks for CreatePropertyField.
|
||||
func (ps *PropertyService) runPreCreatePropertyField(rctx request.CTX, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
field, err = hook.PreCreatePropertyField(rctx, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return field, nil
|
||||
}
|
||||
|
||||
// runPreUpdatePropertyField runs all registered pre-hooks for UpdatePropertyField.
|
||||
func (ps *PropertyService) runPreUpdatePropertyField(rctx request.CTX, groupID string, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
field, err = hook.PreUpdatePropertyField(rctx, groupID, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return field, nil
|
||||
}
|
||||
|
||||
// runPreUpdatePropertyFields runs all registered pre-hooks for UpdatePropertyFields.
|
||||
func (ps *PropertyService) runPreUpdatePropertyFields(rctx request.CTX, groupID string, fields []*model.PropertyField) ([]*model.PropertyField, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
fields, err = hook.PreUpdatePropertyFields(rctx, groupID, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fields == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// runPostUpdatePropertyFields runs all registered post-hooks for
|
||||
// UpdatePropertyFields. Each hook may transform the requested and propagated
|
||||
// buckets in place (e.g. redaction); the dispatcher chains the transformed
|
||||
// slices through subsequent hooks and enforces cardinality preservation on
|
||||
// both buckets so a buggy hook that drops fields surfaces an error rather
|
||||
// than silently truncating the broadcast. The cleared field IDs returned by
|
||||
// each hook are deduped into a single slice. Best-effort: hook errors and
|
||||
// cardinality violations are logged and skipped (the offending hook's
|
||||
// transform is dropped for the chain, but the update itself is not rolled
|
||||
// back).
|
||||
func (ps *PropertyService) runPostUpdatePropertyFields(rctx request.CTX, groupID string, prev, requested, propagated []*model.PropertyField) ([]*model.PropertyField, []*model.PropertyField, []string) {
|
||||
seen := map[string]struct{}{}
|
||||
var cleared []string
|
||||
for _, hook := range ps.hooks {
|
||||
newRequested, newPropagated, ids, err := hook.PostUpdatePropertyFields(rctx, groupID, prev, requested, propagated)
|
||||
if err != nil {
|
||||
rctx.Logger().Error("PostUpdatePropertyFields hook failed",
|
||||
mlog.String("group_id", groupID),
|
||||
mlog.Err(err),
|
||||
)
|
||||
continue
|
||||
}
|
||||
if len(newRequested) != len(requested) || len(newPropagated) != len(propagated) {
|
||||
rctx.Logger().Error("PostUpdatePropertyFields hook returned wrong-length slice",
|
||||
mlog.String("group_id", groupID),
|
||||
mlog.Err(errFieldCardinalityBroken),
|
||||
)
|
||||
continue
|
||||
}
|
||||
requested = newRequested
|
||||
propagated = newPropagated
|
||||
for _, id := range ids {
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
cleared = append(cleared, id)
|
||||
}
|
||||
}
|
||||
return requested, propagated, cleared
|
||||
}
|
||||
|
||||
// runPreDeletePropertyField runs all registered pre-hooks for DeletePropertyField.
|
||||
func (ps *PropertyService) runPreDeletePropertyField(rctx request.CTX, groupID string, id string) error {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PreDeletePropertyField(rctx, groupID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runPreCountPropertyFields runs all registered pre-hooks for the public
|
||||
// CountProperty* methods.
|
||||
func (ps *PropertyService) runPreCountPropertyFields(rctx request.CTX, groupID string) error {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PreCountPropertyFields(rctx, groupID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runPostGetPropertyField runs all registered post-hooks for single field retrieval.
|
||||
func (ps *PropertyService) runPostGetPropertyField(rctx request.CTX, field *model.PropertyField) (*model.PropertyField, error) {
|
||||
if field == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
field, err = hook.PostGetPropertyField(rctx, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return field, nil
|
||||
}
|
||||
|
||||
// runPostGetPropertyFields runs all registered post-hooks for multi-field retrieval.
|
||||
// It enforces that hooks preserve slice length — a hook that drops fields is a bug.
|
||||
func (ps *PropertyService) runPostGetPropertyFields(rctx request.CTX, fields []*model.PropertyField) ([]*model.PropertyField, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
n := len(fields)
|
||||
fields, err = hook.PostGetPropertyFields(rctx, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fields) != n {
|
||||
return nil, errFieldCardinalityBroken
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// runPreCreatePropertyValue runs all registered pre-hooks for CreatePropertyValue.
|
||||
func (ps *PropertyService) runPreCreatePropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
value, err = hook.PreCreatePropertyValue(rctx, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// runPreCreatePropertyValues runs all registered pre-hooks for CreatePropertyValues.
|
||||
func (ps *PropertyService) runPreCreatePropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
values, err = hook.PreCreatePropertyValues(rctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if values == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// runPreUpdatePropertyValue runs all registered pre-hooks for UpdatePropertyValue.
|
||||
func (ps *PropertyService) runPreUpdatePropertyValue(rctx request.CTX, groupID string, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
value, err = hook.PreUpdatePropertyValue(rctx, groupID, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// runPreUpdatePropertyValues runs all registered pre-hooks for UpdatePropertyValues.
|
||||
func (ps *PropertyService) runPreUpdatePropertyValues(rctx request.CTX, groupID string, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
values, err = hook.PreUpdatePropertyValues(rctx, groupID, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if values == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// runPreUpsertPropertyValue runs all registered pre-hooks for UpsertPropertyValue.
|
||||
func (ps *PropertyService) runPreUpsertPropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
value, err = hook.PreUpsertPropertyValue(rctx, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// runPreUpsertPropertyValues runs all registered pre-hooks for UpsertPropertyValues.
|
||||
func (ps *PropertyService) runPreUpsertPropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
values, err = hook.PreUpsertPropertyValues(rctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if values == nil {
|
||||
return nil, errNilHookResult
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// runPreDeletePropertyValue runs all registered pre-hooks for DeletePropertyValue.
|
||||
func (ps *PropertyService) runPreDeletePropertyValue(rctx request.CTX, groupID string, id string) error {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PreDeletePropertyValue(rctx, groupID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runPreDeletePropertyValuesForTarget runs all registered pre-hooks for DeletePropertyValuesForTarget.
|
||||
func (ps *PropertyService) runPreDeletePropertyValuesForTarget(rctx request.CTX, groupID string, targetType string, targetID string) error {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PreDeletePropertyValuesForTarget(rctx, groupID, targetType, targetID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runPreDeletePropertyValuesForField runs all registered pre-hooks for DeletePropertyValuesForField.
|
||||
func (ps *PropertyService) runPreDeletePropertyValuesForField(rctx request.CTX, groupID string, fieldID string) error {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PreDeletePropertyValuesForField(rctx, groupID, fieldID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runPostCreatePropertyValue runs all registered post-hooks for CreatePropertyValue.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostCreatePropertyValue(rctx request.CTX, value *model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostCreatePropertyValue(rctx, value); err != nil {
|
||||
rctx.Logger().Error("PostCreatePropertyValue hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostCreatePropertyValues runs all registered post-hooks for CreatePropertyValues.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostCreatePropertyValues(rctx request.CTX, values []*model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostCreatePropertyValues(rctx, values); err != nil {
|
||||
rctx.Logger().Error("PostCreatePropertyValues hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostUpdatePropertyValue runs all registered post-hooks for UpdatePropertyValue.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostUpdatePropertyValue(rctx request.CTX, value *model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostUpdatePropertyValue(rctx, value); err != nil {
|
||||
rctx.Logger().Error("PostUpdatePropertyValue hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostUpdatePropertyValues runs all registered post-hooks for UpdatePropertyValues.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostUpdatePropertyValues(rctx request.CTX, values []*model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostUpdatePropertyValues(rctx, values); err != nil {
|
||||
rctx.Logger().Error("PostUpdatePropertyValues hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostUpsertPropertyValue runs all registered post-hooks for UpsertPropertyValue.
|
||||
// Best-effort: hook errors are logged and skipped; the write is not rolled back.
|
||||
func (ps *PropertyService) runPostUpsertPropertyValue(rctx request.CTX, value *model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostUpsertPropertyValue(rctx, value); err != nil {
|
||||
rctx.Logger().Error("PostUpsertPropertyValue hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostUpsertPropertyValues runs all registered post-hooks for UpsertPropertyValues.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostUpsertPropertyValues(rctx request.CTX, values []*model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostUpsertPropertyValues(rctx, values); err != nil {
|
||||
rctx.Logger().Error("PostUpsertPropertyValues hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostDeletePropertyValue runs all registered post-hooks for DeletePropertyValue.
|
||||
// deleted is the pre-delete snapshot. Best-effort.
|
||||
func (ps *PropertyService) runPostDeletePropertyValue(rctx request.CTX, groupID, valueID string, deleted *model.PropertyValue) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostDeletePropertyValue(rctx, groupID, valueID, deleted); err != nil {
|
||||
rctx.Logger().Error("PostDeletePropertyValue hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostDeletePropertyValuesForTarget runs all registered post-hooks for DeletePropertyValuesForTarget.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostDeletePropertyValuesForTarget(rctx request.CTX, groupID, targetType, targetID string) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostDeletePropertyValuesForTarget(rctx, groupID, targetType, targetID); err != nil {
|
||||
rctx.Logger().Error("PostDeletePropertyValuesForTarget hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostDeletePropertyValuesForField runs all registered post-hooks for DeletePropertyValuesForField.
|
||||
// Best-effort.
|
||||
func (ps *PropertyService) runPostDeletePropertyValuesForField(rctx request.CTX, groupID, fieldID string) {
|
||||
for _, hook := range ps.hooks {
|
||||
if err := hook.PostDeletePropertyValuesForField(rctx, groupID, fieldID); err != nil {
|
||||
rctx.Logger().Error("PostDeletePropertyValuesForField hook failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runPostGetPropertyValue runs all registered post-hooks for single value retrieval.
|
||||
func (ps *PropertyService) runPostGetPropertyValue(rctx request.CTX, value *model.PropertyValue) (*model.PropertyValue, error) {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
value, err = hook.PostGetPropertyValue(rctx, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// runPostGetPropertyValues runs all registered post-hooks for multi-value retrieval.
|
||||
func (ps *PropertyService) runPostGetPropertyValues(rctx request.CTX, values []*model.PropertyValue) ([]*model.PropertyValue, error) {
|
||||
var err error
|
||||
for _, hook := range ps.hooks {
|
||||
values, err = hook.PostGetPropertyValues(rctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
Reference in New Issue
Block a user