mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[feature] filter API v2: Restore keywords_attributes and statuses_attributes (#2995)
These filter API v2 features were cut late in development because the form encoding version is hard to implement correctly and because I thought no clients actually used `keywords_attributes`. Unfortunately, Phanpy does use `keywords_attributes`.
This commit is contained in:
@ -63,6 +63,29 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
||||
}
|
||||
}
|
||||
|
||||
for _, formKeyword := range form.Keywords {
|
||||
filterKeyword := >smodel.FilterKeyword{
|
||||
ID: id.NewULID(),
|
||||
AccountID: account.ID,
|
||||
FilterID: filter.ID,
|
||||
Filter: filter,
|
||||
Keyword: formKeyword.Keyword,
|
||||
WholeWord: formKeyword.WholeWord,
|
||||
}
|
||||
filter.Keywords = append(filter.Keywords, filterKeyword)
|
||||
}
|
||||
|
||||
for _, formStatus := range form.Statuses {
|
||||
filterStatus := >smodel.FilterStatus{
|
||||
ID: id.NewULID(),
|
||||
AccountID: account.ID,
|
||||
FilterID: filter.ID,
|
||||
Filter: filter,
|
||||
StatusID: formStatus.StatusID,
|
||||
}
|
||||
filter.Statuses = append(filter.Statuses, filterStatus)
|
||||
}
|
||||
|
||||
if err := p.state.DB.PutFilter(ctx, filter); err != nil {
|
||||
if errors.Is(err, db.ErrAlreadyExists) {
|
||||
err = errors.New("duplicate title, keyword, or status")
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
@ -39,6 +40,8 @@ func (p *Processor) Update(
|
||||
filterID string,
|
||||
form *apimodel.FilterUpdateRequestV2,
|
||||
) (*apimodel.FilterV2, gtserror.WithCode) {
|
||||
var errWithCode gtserror.WithCode
|
||||
|
||||
// Get the filter by ID, with existing keywords and statuses.
|
||||
filter, err := p.state.DB.GetFilterByID(ctx, filterID)
|
||||
if err != nil {
|
||||
@ -103,13 +106,17 @@ func (p *Processor) Update(
|
||||
}
|
||||
}
|
||||
|
||||
// Temporarily detach keywords and statuses from filter, since we're not updating them below.
|
||||
filterKeywords := filter.Keywords
|
||||
filterStatuses := filter.Statuses
|
||||
filter.Keywords = nil
|
||||
filter.Statuses = nil
|
||||
filterKeywordColumns, deleteFilterKeywordIDs, errWithCode := applyKeywordChanges(filter, form.Keywords)
|
||||
if err != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
if err := p.state.DB.UpdateFilter(ctx, filter, filterColumns, nil, nil, nil); err != nil {
|
||||
deleteFilterStatusIDs, errWithCode := applyStatusChanges(filter, form.Statuses)
|
||||
if err != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
if err := p.state.DB.UpdateFilter(ctx, filter, filterColumns, filterKeywordColumns, deleteFilterKeywordIDs, deleteFilterStatusIDs); err != nil {
|
||||
if errors.Is(err, db.ErrAlreadyExists) {
|
||||
err = errors.New("you already have a filter with this title")
|
||||
return nil, gtserror.NewErrorConflict(err, err.Error())
|
||||
@ -117,10 +124,6 @@ func (p *Processor) Update(
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Re-attach keywords and statuses before returning.
|
||||
filter.Keywords = filterKeywords
|
||||
filter.Statuses = filterStatuses
|
||||
|
||||
apiFilter, errWithCode := p.apiFilter(ctx, filter)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
@ -131,3 +134,131 @@ func (p *Processor) Update(
|
||||
|
||||
return apiFilter, nil
|
||||
}
|
||||
|
||||
// applyKeywordChanges applies the provided changes to the filter's keywords in place,
|
||||
// and returns a list of lists of filter columns to update, and a list of filter keyword IDs to delete.
|
||||
func applyKeywordChanges(filter *gtsmodel.Filter, formKeywords []apimodel.FilterKeywordCreateUpdateDeleteRequest) ([][]string, []string, gtserror.WithCode) {
|
||||
if len(formKeywords) == 0 {
|
||||
// Detach currently existing keywords from the filter so we don't change them.
|
||||
filter.Keywords = nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
deleteFilterKeywordIDs := []string{}
|
||||
filterKeywordsByID := map[string]*gtsmodel.FilterKeyword{}
|
||||
filterKeywordColumnsByID := map[string][]string{}
|
||||
for _, filterKeyword := range filter.Keywords {
|
||||
filterKeywordsByID[filterKeyword.ID] = filterKeyword
|
||||
}
|
||||
|
||||
for _, formKeyword := range formKeywords {
|
||||
if formKeyword.ID != nil {
|
||||
id := *formKeyword.ID
|
||||
filterKeyword, ok := filterKeywordsByID[id]
|
||||
if !ok {
|
||||
return nil, nil, gtserror.NewErrorNotFound(
|
||||
fmt.Errorf("couldn't find filter keyword '%s' to update or delete", id),
|
||||
)
|
||||
}
|
||||
|
||||
// Process deletes.
|
||||
if *formKeyword.Destroy {
|
||||
delete(filterKeywordsByID, id)
|
||||
deleteFilterKeywordIDs = append(deleteFilterKeywordIDs, id)
|
||||
continue
|
||||
}
|
||||
|
||||
// Process updates.
|
||||
columns := make([]string, 0, 2)
|
||||
if formKeyword.Keyword != nil {
|
||||
columns = append(columns, "keyword")
|
||||
filterKeyword.Keyword = *formKeyword.Keyword
|
||||
}
|
||||
if formKeyword.WholeWord != nil {
|
||||
columns = append(columns, "whole_word")
|
||||
filterKeyword.WholeWord = formKeyword.WholeWord
|
||||
}
|
||||
filterKeywordColumnsByID[id] = columns
|
||||
continue
|
||||
}
|
||||
|
||||
// Process creates.
|
||||
filterKeyword := >smodel.FilterKeyword{
|
||||
ID: id.NewULID(),
|
||||
AccountID: filter.AccountID,
|
||||
FilterID: filter.ID,
|
||||
Filter: filter,
|
||||
Keyword: *formKeyword.Keyword,
|
||||
WholeWord: util.Ptr(util.PtrValueOr(formKeyword.WholeWord, false)),
|
||||
}
|
||||
filterKeywordsByID[filterKeyword.ID] = filterKeyword
|
||||
// Don't need to set columns, as we're using all of them.
|
||||
}
|
||||
|
||||
// Replace the filter's keywords list with our updated version.
|
||||
filterKeywordColumns := [][]string{}
|
||||
filter.Keywords = nil
|
||||
for id, filterKeyword := range filterKeywordsByID {
|
||||
filter.Keywords = append(filter.Keywords, filterKeyword)
|
||||
// Okay to use the nil slice zero value for entries being created instead of updated.
|
||||
filterKeywordColumns = append(filterKeywordColumns, filterKeywordColumnsByID[id])
|
||||
}
|
||||
|
||||
return filterKeywordColumns, deleteFilterKeywordIDs, nil
|
||||
}
|
||||
|
||||
// applyKeywordChanges applies the provided changes to the filter's keywords in place,
|
||||
// and returns a list of filter status IDs to delete.
|
||||
func applyStatusChanges(filter *gtsmodel.Filter, formStatuses []apimodel.FilterStatusCreateDeleteRequest) ([]string, gtserror.WithCode) {
|
||||
if len(formStatuses) == 0 {
|
||||
// Detach currently existing statuses from the filter so we don't change them.
|
||||
filter.Statuses = nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
deleteFilterStatusIDs := []string{}
|
||||
filterStatusesByID := map[string]*gtsmodel.FilterStatus{}
|
||||
for _, filterStatus := range filter.Statuses {
|
||||
filterStatusesByID[filterStatus.ID] = filterStatus
|
||||
}
|
||||
|
||||
for _, formStatus := range formStatuses {
|
||||
if formStatus.ID != nil {
|
||||
id := *formStatus.ID
|
||||
_, ok := filterStatusesByID[id]
|
||||
if !ok {
|
||||
return nil, gtserror.NewErrorNotFound(
|
||||
fmt.Errorf("couldn't find filter status '%s' to delete", id),
|
||||
)
|
||||
}
|
||||
|
||||
// Process deletes.
|
||||
if *formStatus.Destroy {
|
||||
delete(filterStatusesByID, id)
|
||||
deleteFilterStatusIDs = append(deleteFilterStatusIDs, id)
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter statuses don't have updates.
|
||||
continue
|
||||
}
|
||||
|
||||
// Process creates.
|
||||
filterStatus := >smodel.FilterStatus{
|
||||
ID: id.NewULID(),
|
||||
AccountID: filter.AccountID,
|
||||
FilterID: filter.ID,
|
||||
Filter: filter,
|
||||
StatusID: *formStatus.StatusID,
|
||||
}
|
||||
filterStatusesByID[filterStatus.ID] = filterStatus
|
||||
}
|
||||
|
||||
// Replace the filter's keywords list with our updated version.
|
||||
filter.Statuses = nil
|
||||
for _, filterStatus := range filterStatusesByID {
|
||||
filter.Statuses = append(filter.Statuses, filterStatus)
|
||||
}
|
||||
|
||||
return deleteFilterStatusIDs, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user