summaryrefslogtreecommitdiff
path: root/internal/processing/filters/common
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-06-24 17:24:34 +0200
committerLibravatar tobi <kipvandenbos@noreply.codeberg.org>2025-06-24 17:24:34 +0200
commit996da6e0291b158093d917ca76933584f464d668 (patch)
tree38c12b20f76076f08ef5c8b8715ba3d8629fa0fb /internal/processing/filters/common
parent[bugfix] update the default configuration to not set a db type or address, to... (diff)
downloadgotosocial-996da6e0291b158093d917ca76933584f464d668.tar.xz
[performance] filter model and database table improvements (#4277)
- removes unnecessary fields / columns (created_at, updated_at) - replaces filter.context_* columns with singular filter.contexts bit field which should save both struct memory and database space - replaces filter.action string with integer enum type which should save both struct memory and database space - adds links from filter to filter_* tables with Filter{}.KeywordIDs and Filter{}.StatusIDs fields (this also means we now have those ID slices cached, which reduces some lookups) - removes account_id fields from filter_* tables, since there's a more direct connection between filter and filter_* tables, and filter.account_id already exists - refactors a bunch of the filter processor logic to save on code repetition, factor in the above changes, fix a few bugs with missed error returns and bring it more in-line with some of our newer code Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4277 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal/processing/filters/common')
-rw-r--r--internal/processing/filters/common/common.go184
1 files changed, 184 insertions, 0 deletions
diff --git a/internal/processing/filters/common/common.go b/internal/processing/filters/common/common.go
new file mode 100644
index 000000000..a119d3bd4
--- /dev/null
+++ b/internal/processing/filters/common/common.go
@@ -0,0 +1,184 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http"
+
+ apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
+ "code.superseriousbusiness.org/gotosocial/internal/db"
+ "code.superseriousbusiness.org/gotosocial/internal/gtscontext"
+ "code.superseriousbusiness.org/gotosocial/internal/gtserror"
+ "code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
+ "code.superseriousbusiness.org/gotosocial/internal/state"
+)
+
+type Processor struct{ state *state.State }
+
+func New(state *state.State) *Processor { return &Processor{state} }
+
+// CheckFilterExists calls .GetFilter() with a barebones context to not
+// fetch any sub-models, and not returning the result. this functionally
+// just uses .GetFilter() for the ownership and existence checks.
+func (p *Processor) CheckFilterExists(
+ ctx context.Context,
+ requester *gtsmodel.Account,
+ id string,
+) gtserror.WithCode {
+ _, errWithCode := p.GetFilter(gtscontext.SetBarebones(ctx), requester, id)
+ return errWithCode
+}
+
+// GetFilter fetches the filter with given ID, also checking
+// the given requesting account is the owner of the filter.
+func (p *Processor) GetFilter(
+ ctx context.Context,
+ requester *gtsmodel.Account,
+ id string,
+) (
+ *gtsmodel.Filter,
+ gtserror.WithCode,
+) {
+ // Get the filter from the database with given ID.
+ filter, err := p.state.DB.GetFilterByID(ctx, id)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := gtserror.Newf("error getting filter: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // Check it exists.
+ if filter == nil {
+ const text = "filter not found"
+ return nil, gtserror.NewWithCode(http.StatusNotFound, text)
+ }
+
+ // Check that the requester owns it.
+ if filter.AccountID != requester.ID {
+ const text = "filter not found"
+ err := gtserror.New("filter does not belong to account")
+ return nil, gtserror.NewErrorNotFound(err, text)
+ }
+
+ return filter, nil
+}
+
+// GetFilterStatus fetches the filter status with given ID, also
+// checking the given requesting account is the owner of it.
+func (p *Processor) GetFilterStatus(
+ ctx context.Context,
+ requester *gtsmodel.Account,
+ id string,
+) (
+ *gtsmodel.FilterStatus,
+ *gtsmodel.Filter,
+ gtserror.WithCode,
+) {
+
+ // Get the filter status from the database with given ID.
+ filterStatus, err := p.state.DB.GetFilterStatusByID(ctx, id)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := gtserror.Newf("error getting filter status: %w", err)
+ return nil, nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // Check it even exists.
+ if filterStatus == nil {
+ const text = "filter status not found"
+ return nil, nil, gtserror.NewWithCode(http.StatusNotFound, text)
+ }
+
+ // Get the filter this filter status is
+ // associated with, without sub-models.
+ // (this also checks filter ownership).
+ filter, errWithCode := p.GetFilter(
+ gtscontext.SetBarebones(ctx),
+ requester,
+ filterStatus.FilterID,
+ )
+ if errWithCode != nil {
+ return nil, nil, errWithCode
+ }
+
+ return filterStatus, filter, nil
+}
+
+// GetFilterKeyword fetches the filter keyword with given ID,
+// also checking the given requesting account is the owner of it.
+func (p *Processor) GetFilterKeyword(
+ ctx context.Context,
+ requester *gtsmodel.Account,
+ id string,
+) (
+ *gtsmodel.FilterKeyword,
+ *gtsmodel.Filter,
+ gtserror.WithCode,
+) {
+
+ // Get the filter keyword from the database with given ID.
+ keyword, err := p.state.DB.GetFilterKeywordByID(ctx, id)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := gtserror.Newf("error getting filter keyword: %w", err)
+ return nil, nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // Check it exists.
+ if keyword == nil {
+ const text = "filter keyword not found"
+ return nil, nil, gtserror.NewWithCode(http.StatusNotFound, text)
+ }
+
+ // Get the filter this filter keyword is
+ // associated with, without sub-models.
+ // (this also checks filter ownership).
+ filter, errWithCode := p.GetFilter(
+ gtscontext.SetBarebones(ctx),
+ requester,
+ keyword.FilterID,
+ )
+ if errWithCode != nil {
+ return nil, nil, errWithCode
+ }
+
+ return keyword, filter, nil
+}
+
+// FromAPIContexts converts a slice of frontend API model FilterContext types to our internal FilterContexts bit field.
+func FromAPIContexts(apiContexts []apimodel.FilterContext) (gtsmodel.FilterContexts, gtserror.WithCode) {
+ var contexts gtsmodel.FilterContexts
+ for _, context := range apiContexts {
+ switch context {
+ case apimodel.FilterContextHome:
+ contexts.SetHome()
+ case apimodel.FilterContextNotifications:
+ contexts.SetNotifications()
+ case apimodel.FilterContextPublic:
+ contexts.SetPublic()
+ case apimodel.FilterContextThread:
+ contexts.SetThread()
+ case apimodel.FilterContextAccount:
+ contexts.SetAccount()
+ default:
+ text := fmt.Sprintf("unsupported filter context: %s", context)
+ return 0, gtserror.NewWithCode(http.StatusBadRequest, text)
+ }
+ }
+ return contexts, nil
+}