get.go (4886B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package list 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 25 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 26 "github.com/superseriousbusiness/gotosocial/internal/db" 27 "github.com/superseriousbusiness/gotosocial/internal/gtscontext" 28 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 29 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 30 "github.com/superseriousbusiness/gotosocial/internal/log" 31 "github.com/superseriousbusiness/gotosocial/internal/util" 32 ) 33 34 // Get returns the api model of one list with the given ID. 35 func (p *Processor) Get(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.List, gtserror.WithCode) { 36 list, errWithCode := p.getList( 37 // Use barebones ctx; no embedded 38 // structs necessary for this call. 39 gtscontext.SetBarebones(ctx), 40 account.ID, 41 id, 42 ) 43 if errWithCode != nil { 44 return nil, errWithCode 45 } 46 47 return p.apiList(ctx, list) 48 } 49 50 // GetMultiple returns multiple lists created by the given account, sorted by list ID DESC (newest first). 51 func (p *Processor) GetAll(ctx context.Context, account *gtsmodel.Account) ([]*apimodel.List, gtserror.WithCode) { 52 lists, err := p.state.DB.GetListsForAccountID( 53 // Use barebones ctx; no embedded 54 // structs necessary for simple GET. 55 gtscontext.SetBarebones(ctx), 56 account.ID, 57 ) 58 if err != nil { 59 if errors.Is(err, db.ErrNoEntries) { 60 return nil, nil 61 } 62 return nil, gtserror.NewErrorInternalError(err) 63 } 64 65 apiLists := make([]*apimodel.List, 0, len(lists)) 66 for _, list := range lists { 67 apiList, errWithCode := p.apiList(ctx, list) 68 if errWithCode != nil { 69 return nil, errWithCode 70 } 71 72 apiLists = append(apiLists, apiList) 73 } 74 75 return apiLists, nil 76 } 77 78 // GetListAccounts returns accounts that are in the given list, owned by the given account. 79 // The additional parameters can be used for paging. 80 func (p *Processor) GetListAccounts( 81 ctx context.Context, 82 account *gtsmodel.Account, 83 listID string, 84 maxID string, 85 sinceID string, 86 minID string, 87 limit int, 88 ) (*apimodel.PageableResponse, gtserror.WithCode) { 89 // Ensure list exists + is owned by requesting account. 90 if _, errWithCode := p.getList(ctx, account.ID, listID); errWithCode != nil { 91 return nil, errWithCode 92 } 93 94 // To know which accounts are in the list, 95 // we need to first get requested list entries. 96 listEntries, err := p.state.DB.GetListEntries(ctx, listID, maxID, sinceID, minID, limit) 97 if err != nil && !errors.Is(err, db.ErrNoEntries) { 98 err = fmt.Errorf("GetListAccounts: error getting list entries: %w", err) 99 return nil, gtserror.NewErrorInternalError(err) 100 } 101 102 count := len(listEntries) 103 if count == 0 { 104 // No list entries means no accounts. 105 return util.EmptyPageableResponse(), nil 106 } 107 108 var ( 109 items = make([]interface{}, count) 110 nextMaxIDValue string 111 prevMinIDValue string 112 ) 113 114 // For each list entry, we want the account it points to. 115 // To get this, we need to first get the follow that the 116 // list entry pertains to, then extract the target account 117 // from that follow. 118 // 119 // We do paging not by account ID, but by list entry ID. 120 for i, listEntry := range listEntries { 121 if i == count-1 { 122 nextMaxIDValue = listEntry.ID 123 } 124 125 if i == 0 { 126 prevMinIDValue = listEntry.ID 127 } 128 129 if err := p.state.DB.PopulateListEntry(ctx, listEntry); err != nil { 130 log.Debugf(ctx, "skipping list entry because of error populating it: %q", err) 131 continue 132 } 133 134 if err := p.state.DB.PopulateFollow(ctx, listEntry.Follow); err != nil { 135 log.Debugf(ctx, "skipping list entry because of error populating follow: %q", err) 136 continue 137 } 138 139 apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, listEntry.Follow.TargetAccount) 140 if err != nil { 141 log.Debugf(ctx, "skipping list entry because of error converting follow target account: %q", err) 142 continue 143 } 144 145 items[i] = apiAccount 146 } 147 148 return util.PackagePageableResponse(util.PageableResponseParams{ 149 Items: items, 150 Path: "api/v1/lists/" + listID + "/accounts", 151 NextMaxIDValue: nextMaxIDValue, 152 PrevMinIDValue: prevMinIDValue, 153 Limit: limit, 154 }) 155 }