[feature] Account alias / move API + db models (#2518)

* [feature] Account alias / move API + db models

* go fmt

* fix little cherry-pick issues

* update error checking, formatting

* add and use new util functions to simplify alias logic
This commit is contained in:
tobi
2024-01-16 17:22:44 +01:00
committed by GitHub
parent ebf550b7c1
commit c36f9ac37b
23 changed files with 1243 additions and 39 deletions

View File

@@ -90,6 +90,7 @@ func (c *Converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode
Note: a.NoteRaw,
Fields: c.fieldsToAPIFields(a.FieldsRaw),
FollowRequestsCount: frc,
AlsoKnownAsURIs: a.AlsoKnownAsURIs,
}
return apiAccount, nil
@@ -111,27 +112,27 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
followersCount, err := c.state.DB.CountAccountFollowers(ctx, a.ID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting followers: %w", err)
return nil, gtserror.Newf("error counting followers: %w", err)
}
followingCount, err := c.state.DB.CountAccountFollows(ctx, a.ID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting following: %w", err)
return nil, gtserror.Newf("error counting following: %w", err)
}
statusesCount, err := c.state.DB.CountAccountStatuses(ctx, a.ID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting statuses: %w", err)
return nil, gtserror.Newf("error counting statuses: %w", err)
}
var lastStatusAt *string
lastPosted, err := c.state.DB.GetAccountLastPosted(ctx, a.ID, false)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting statuses: %w", err)
return nil, gtserror.Newf("error getting last posted: %w", err)
}
if !lastPosted.IsZero() {
lastStatusAt = func() *string { t := util.FormatISO8601(lastPosted); return &t }()
lastStatusAt = util.Ptr(util.FormatISO8601(lastPosted))
}
// Profile media + nice extras:
@@ -180,7 +181,7 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
// de-punify it just in case.
d, err := util.DePunify(a.Domain)
if err != nil {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err)
return nil, gtserror.Newf("error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err)
}
acct = a.Username + "@" + d
@@ -191,7 +192,7 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
if !a.IsInstance() {
user, err := c.state.DB.GetUserByAccountID(ctx, a.ID)
if err != nil {
return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err)
return nil, gtserror.Newf("error getting user from database for account id %s: %w", a.ID, err)
}
switch {
@@ -207,6 +208,15 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
acct = a.Username // omit domain
}
// Populate moved.
var moved *apimodel.Account
if a.MovedTo != nil {
moved, err = c.AccountToAPIAccountPublic(ctx, a.MovedTo)
if err != nil {
log.Errorf(ctx, "error converting account movedTo: %v", err)
}
}
// Remaining properties are simple and
// can be populated directly below.
@@ -235,6 +245,7 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
CustomCSS: a.CustomCSS,
EnableRSS: *a.EnableRSS,
Role: role,
Moved: moved,
}
// Bodge default avatar + header in,

View File

@@ -69,6 +69,105 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontend() {
}`, string(b))
}
func (suite *InternalToFrontendTestSuite) TestAccountToFrontendAliasedAndMoved() {
// Take zork for this test.
var testAccount = new(gtsmodel.Account)
*testAccount = *suite.testAccounts["local_account_1"]
// Update zork to indicate that he's moved to turtle.
// This is a bit weird but it's just for this test.
movedTo := suite.testAccounts["local_account_2"]
testAccount.MovedToURI = movedTo.URI
testAccount.AlsoKnownAsURIs = []string{movedTo.URI}
if err := suite.state.DB.UpdateAccount(context.Background(), testAccount, "moved_to_uri"); err != nil {
suite.FailNow(err.Error())
}
apiAccount, err := suite.typeconverter.AccountToAPIAccountSensitive(context.Background(), testAccount)
suite.NoError(err)
suite.NotNil(apiAccount)
// moved and also_known_as_uris
// should both be set now.
b, err := json.MarshalIndent(apiAccount, "", " ")
suite.NoError(err)
suite.Equal(`{
"id": "01F8MH1H7YV1Z7D2C8K2730QBF",
"username": "the_mighty_zork",
"acct": "the_mighty_zork",
"display_name": "original zork (he/they)",
"locked": false,
"discoverable": true,
"bot": false,
"created_at": "2022-05-20T11:09:18.000Z",
"note": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
"url": "http://localhost:8080/@the_mighty_zork",
"avatar": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg",
"avatar_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpg",
"header": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"header_static": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg",
"followers_count": 2,
"following_count": 2,
"statuses_count": 7,
"last_status_at": "2023-12-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"source": {
"privacy": "public",
"sensitive": false,
"language": "en",
"status_content_type": "text/plain",
"note": "hey yo this is my profile!",
"fields": [],
"follow_requests_count": 0,
"also_known_as_uris": [
"http://localhost:8080/users/1happyturtle"
]
},
"enable_rss": true,
"role": {
"name": "user"
},
"moved": {
"id": "01F8MH5NBDF2MV7CTC4Q5128HF",
"username": "1happyturtle",
"acct": "1happyturtle",
"display_name": "happy little turtle :3",
"locked": true,
"discoverable": false,
"bot": false,
"created_at": "2022-06-04T13:12:00.000Z",
"note": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
"url": "http://localhost:8080/@1happyturtle",
"avatar": "",
"avatar_static": "",
"header": "http://localhost:8080/assets/default_header.png",
"header_static": "http://localhost:8080/assets/default_header.png",
"followers_count": 1,
"following_count": 1,
"statuses_count": 8,
"last_status_at": "2021-07-28T08:40:37.000Z",
"emojis": [],
"fields": [
{
"name": "should you follow me?",
"value": "maybe!",
"verified_at": null
},
{
"name": "age",
"value": "120",
"verified_at": null
}
],
"role": {
"name": "user"
}
}
}`, string(b))
}
func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiStruct() {
testAccount := &gtsmodel.Account{}
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test