diff options
| author | 2022-11-29 17:59:59 +0000 | |
|---|---|---|
| committer | 2022-11-29 17:59:59 +0000 | |
| commit | cfa8d7900c5d88a2b126b3ea74784bd589d05f34 (patch) | |
| tree | fad02234040192821844780d06cfd9f4b16974b2 /internal | |
| parent | [bugfix] wrap the correct error on failed account update (#1176) (diff) | |
| download | gotosocial-cfa8d7900c5d88a2b126b3ea74784bd589d05f34.tar.xz | |
[bugfix] don't return error during account serialize on deleted emoji (#1177)
* only return error for emoji fetch if NOT errnoentries
Signed-off-by: kim <grufwub@gmail.com>
* reformat gts->api model slice conversion to standard error behaviours and reduce code reuse
Signed-off-by: kim <grufwub@gmail.com>
Signed-off-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/typeutils/internaltofrontend.go | 371 | 
1 files changed, 210 insertions, 161 deletions
| diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 9c55a55f4..d6938ae83 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -82,10 +82,6 @@ func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode  }  func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.Account) (*model.Account, error) { -	if a == nil { -		return nil, fmt.Errorf("given account was nil") -	} -  	// count followers  	followersCount, err := c.db.CountAccountFollowedBy(ctx, a.ID, false)  	if err != nil { @@ -118,11 +114,10 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A  	if a.AvatarMediaAttachmentID != "" {  		if a.AvatarMediaAttachment == nil {  			avi, err := c.db.GetAttachmentByID(ctx, a.AvatarMediaAttachmentID) -			if err == nil { -				a.AvatarMediaAttachment = avi -			} else { +			if err != nil {  				log.Errorf("AccountToAPIAccountPublic: error getting Avatar with id %s: %s", a.AvatarMediaAttachmentID, err)  			} +			a.AvatarMediaAttachment = avi  		}  		if a.AvatarMediaAttachment != nil {  			aviURL = a.AvatarMediaAttachment.URL @@ -136,11 +131,10 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A  	if a.HeaderMediaAttachmentID != "" {  		if a.HeaderMediaAttachment == nil {  			avi, err := c.db.GetAttachmentByID(ctx, a.HeaderMediaAttachmentID) -			if err == nil { -				a.HeaderMediaAttachment = avi -			} else { +			if err != nil {  				log.Errorf("AccountToAPIAccountPublic: error getting Header with id %s: %s", a.HeaderMediaAttachmentID, err)  			} +			a.HeaderMediaAttachment = avi  		}  		if a.HeaderMediaAttachment != nil {  			headerURL = a.HeaderMediaAttachment.URL @@ -148,41 +142,25 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A  		}  	} -	// get the fields set on this account -	fields := []model.Field{} -	for _, f := range a.Fields { +	// preallocate frontend fields slice +	fields := make([]model.Field, len(a.Fields)) + +	// Convert account GTS model fields to frontend +	for i, field := range a.Fields {  		mField := model.Field{ -			Name:  f.Name, -			Value: f.Value, +			Name:  field.Name, +			Value: field.Value,  		} -		if !f.VerifiedAt.IsZero() { -			mField.VerifiedAt = util.FormatISO8601(f.VerifiedAt) +		if !field.VerifiedAt.IsZero() { +			mField.VerifiedAt = util.FormatISO8601(field.VerifiedAt)  		} -		fields = append(fields, mField) +		fields[i] = mField  	} -	// account emojis -	emojis := []model.Emoji{} -	gtsEmojis := a.Emojis -	if len(a.EmojiIDs) > len(gtsEmojis) { -		gtsEmojis = []*gtsmodel.Emoji{} -		for _, emojiID := range a.EmojiIDs { -			emoji, err := c.db.GetEmojiByID(ctx, emojiID) -			if err != nil { -				return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting emoji %s from database: %s", emojiID, err) -			} -			gtsEmojis = append(gtsEmojis, emoji) -		} -	} -	for _, emoji := range gtsEmojis { -		if *emoji.Disabled { -			continue -		} -		apiEmoji, err := c.EmojiToAPIEmoji(ctx, emoji) -		if err != nil { -			return nil, fmt.Errorf("AccountToAPIAccountPublic: error converting emoji to api emoji: %s", err) -		} -		emojis = append(emojis, apiEmoji) +	// convert account gts model emojis to frontend api model emojis +	apiEmojis, err := c.convertEmojisToAPIEmojis(ctx, a.Emojis, a.EmojiIDs) +	if err != nil { +		log.Errorf("error converting account emojis: %v", err)  	}  	var ( @@ -234,7 +212,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A  		FollowingCount: followingCount,  		StatusesCount:  statusesCount,  		LastStatusAt:   lastStatusAt, -		Emojis:         emojis, +		Emojis:         apiEmojis,  		Fields:         fields,  		Suspended:      suspended,  		CustomCSS:      a.CustomCSS, @@ -497,130 +475,37 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r  		return nil, fmt.Errorf("error parsing account of status author: %s", err)  	} -	apiAttachments := []model.Attachment{} -	// the status might already have some gts attachments on it if it's not been pulled directly from the database -	// if so, we can directly convert the gts attachments into api ones -	if s.Attachments != nil { -		for _, gtsAttachment := range s.Attachments { -			apiAttachment, err := c.AttachmentToAPIAttachment(ctx, gtsAttachment) -			if err != nil { -				log.Errorf("error converting attachment with id %s: %s", gtsAttachment.ID, err) -				continue -			} -			apiAttachments = append(apiAttachments, apiAttachment) -		} -		// the status doesn't have gts attachments on it, but it does have attachment IDs -		// in this case, we need to pull the gts attachments from the db to convert them into api ones -	} else { -		for _, aID := range s.AttachmentIDs { -			gtsAttachment, err := c.db.GetAttachmentByID(ctx, aID) -			if err != nil { -				log.Errorf("error getting attachment with id %s: %s", aID, err) -				continue -			} -			apiAttachment, err := c.AttachmentToAPIAttachment(ctx, gtsAttachment) -			if err != nil { -				log.Errorf("error converting attachment with id %s: %s", aID, err) -				continue -			} -			apiAttachments = append(apiAttachments, apiAttachment) -		} +	// convert status gts model attachments to frontend api model attachments +	apiAttachments, err := c.convertAttachmentsToAPIAttachments(ctx, s.Attachments, s.AttachmentIDs) +	if err != nil { +		log.Errorf("error converting status attachments: %v", err)  	} -	apiMentions := []model.Mention{} -	// the status might already have some gts mentions on it if it's not been pulled directly from the database -	// if so, we can directly convert the gts mentions into api ones -	if s.Mentions != nil { -		for _, gtsMention := range s.Mentions { -			apiMention, err := c.MentionToAPIMention(ctx, gtsMention) -			if err != nil { -				log.Errorf("error converting mention with id %s: %s", gtsMention.ID, err) -				continue -			} -			apiMentions = append(apiMentions, apiMention) -		} -		// the status doesn't have gts mentions on it, but it does have mention IDs -		// in this case, we need to pull the gts mentions from the db to convert them into api ones -	} else { -		for _, mID := range s.MentionIDs { -			gtsMention, err := c.db.GetMention(ctx, mID) -			if err != nil { -				log.Errorf("error getting mention with id %s: %s", mID, err) -				continue -			} -			apiMention, err := c.MentionToAPIMention(ctx, gtsMention) -			if err != nil { -				log.Errorf("error converting mention with id %s: %s", gtsMention.ID, err) -				continue -			} -			apiMentions = append(apiMentions, apiMention) -		} +	// convert status gts model mentions to frontend api model mentions +	apiMentions, err := c.convertMentionsToAPIMentions(ctx, s.Mentions, s.MentionIDs) +	if err != nil { +		log.Errorf("error converting status mentions: %v", err)  	} -	apiTags := []model.Tag{} -	// the status might already have some gts tags on it if it's not been pulled directly from the database -	// if so, we can directly convert the gts tags into api ones -	if s.Tags != nil { -		for _, gtsTag := range s.Tags { -			apiTag, err := c.TagToAPITag(ctx, gtsTag) -			if err != nil { -				log.Errorf("error converting tag with id %s: %s", gtsTag.ID, err) -				continue -			} -			apiTags = append(apiTags, apiTag) -		} -		// the status doesn't have gts tags on it, but it does have tag IDs -		// in this case, we need to pull the gts tags from the db to convert them into api ones -	} else { -		for _, t := range s.TagIDs { -			gtsTag := >smodel.Tag{} -			if err := c.db.GetByID(ctx, t, gtsTag); err != nil { -				log.Errorf("error getting tag with id %s: %s", t, err) -				continue -			} -			apiTag, err := c.TagToAPITag(ctx, gtsTag) -			if err != nil { -				log.Errorf("error converting tag with id %s: %s", gtsTag.ID, err) -				continue -			} -			apiTags = append(apiTags, apiTag) -		} +	// convert status gts model tags to frontend api model tags +	apiTags, err := c.convertTagsToAPITags(ctx, s.Tags, s.TagIDs) +	if err != nil { +		log.Errorf("error converting status tags: %v", err)  	} -	apiEmojis := []model.Emoji{} -	// the status might already have some gts emojis on it if it's not been pulled directly from the database -	// if so, we can directly convert the gts emojis into api ones -	if s.Emojis != nil { -		for _, gtsEmoji := range s.Emojis { -			apiEmoji, err := c.EmojiToAPIEmoji(ctx, gtsEmoji) -			if err != nil { -				log.Errorf("error converting emoji with id %s: %s", gtsEmoji.ID, err) -				continue -			} -			apiEmojis = append(apiEmojis, apiEmoji) -		} -		// the status doesn't have gts emojis on it, but it does have emoji IDs -		// in this case, we need to pull the gts emojis from the db to convert them into api ones -	} else { -		for _, e := range s.EmojiIDs { -			gtsEmoji := >smodel.Emoji{} -			if err := c.db.GetByID(ctx, e, gtsEmoji); err != nil { -				log.Errorf("error getting emoji with id %s: %s", e, err) -				continue -			} -			apiEmoji, err := c.EmojiToAPIEmoji(ctx, gtsEmoji) -			if err != nil { -				log.Errorf("error converting emoji with id %s: %s", gtsEmoji.ID, err) -				continue -			} -			apiEmojis = append(apiEmojis, apiEmoji) -		} +	// convert status gts model emojis to frontend api model emojis +	apiEmojis, err := c.convertEmojisToAPIEmojis(ctx, s.Emojis, s.EmojiIDs) +	if err != nil { +		log.Errorf("error converting status emojis: %v", err)  	} -	statusInteractions := &statusInteractions{} -	si, err := c.interactionsWithStatusForAccount(ctx, s, requestingAccount) -	if err == nil { -		statusInteractions = si +	// Fetch status interaction flags for acccount +	interacts, err := c.interactionsWithStatusForAccount(ctx, s, requestingAccount) +	if err != nil { +		log.Errorf("error getting interactions for status %s for account %s: %v", s.ID, requestingAccount.ID, err) + +		// Ensure a non nil object +		interacts = &statusInteractions{}  	}  	apiStatus := &model.Status{ @@ -637,10 +522,10 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r  		RepliesCount:       repliesCount,  		ReblogsCount:       reblogsCount,  		FavouritesCount:    favesCount, -		Favourited:         statusInteractions.Faved, -		Bookmarked:         statusInteractions.Bookmarked, -		Muted:              statusInteractions.Muted, -		Reblogged:          statusInteractions.Reblogged, +		Favourited:         interacts.Faved, +		Bookmarked:         interacts.Bookmarked, +		Muted:              interacts.Muted, +		Reblogged:          interacts.Reblogged,  		Pinned:             *s.Pinned,  		Content:            s.Content,  		Reblog:             nil, @@ -896,3 +781,167 @@ func (c *converter) DomainBlockToAPIDomainBlock(ctx context.Context, b *gtsmodel  	return domainBlock, nil  } + +// convertAttachmentsToAPIAttachments will convert a slice of GTS model attachments to frontend API model attachments, falling back to IDs if no GTS models supplied. +func (c *converter) convertAttachmentsToAPIAttachments(ctx context.Context, attachments []*gtsmodel.MediaAttachment, attachmentIDs []string) ([]model.Attachment, error) { +	var errs multiError + +	if len(attachments) == 0 { +		// GTS model attachments were not populated + +		// Preallocate expected GTS slice +		attachments = make([]*gtsmodel.MediaAttachment, 0, len(attachmentIDs)) + +		// Fetch GTS models for attachment IDs +		for _, id := range attachmentIDs { +			attachment, err := c.db.GetAttachmentByID(ctx, id) +			if err != nil { +				errs.Appendf("error fetching attachment %s from database: %v", id, err) +				continue +			} +			attachments = append(attachments, attachment) +		} +	} + +	// Preallocate expected frontend slice +	apiAttachments := make([]model.Attachment, 0, len(attachments)) + +	// Convert GTS models to frontend models +	for _, attachment := range attachments { +		apiAttachment, err := c.AttachmentToAPIAttachment(ctx, attachment) +		if err != nil { +			errs.Appendf("error converting attchment %s to api attachment: %v", attachment.ID, err) +			continue +		} +		apiAttachments = append(apiAttachments, apiAttachment) +	} + +	return apiAttachments, errs.Combine() +} + +// convertEmojisToAPIEmojis will convert a slice of GTS model emojis to frontend API model emojis, falling back to IDs if no GTS models supplied. +func (c *converter) convertEmojisToAPIEmojis(ctx context.Context, emojis []*gtsmodel.Emoji, emojiIDs []string) ([]model.Emoji, error) { +	var errs multiError + +	if len(emojis) == 0 { +		// GTS model attachments were not populated + +		// Preallocate expected GTS slice +		emojis = make([]*gtsmodel.Emoji, 0, len(emojiIDs)) + +		// Fetch GTS models for emoji IDs +		for _, id := range emojiIDs { +			emoji, err := c.db.GetEmojiByID(ctx, id) +			if err != nil { +				errs.Appendf("error fetching emoji %s from database: %v", id, err) +				continue +			} +			emojis = append(emojis, emoji) +		} +	} + +	// Preallocate expected frontend slice +	apiEmojis := make([]model.Emoji, 0, len(emojis)) + +	// Convert GTS models to frontend models +	for _, emoji := range emojis { +		apiEmoji, err := c.EmojiToAPIEmoji(ctx, emoji) +		if err != nil { +			errs.Appendf("error converting emoji %s to api emoji: %v", emoji.ID, err) +			continue +		} +		apiEmojis = append(apiEmojis, apiEmoji) +	} + +	return apiEmojis, errs.Combine() +} + +// convertMentionsToAPIMentions will convert a slice of GTS model mentions to frontend API model mentions, falling back to IDs if no GTS models supplied. +func (c *converter) convertMentionsToAPIMentions(ctx context.Context, mentions []*gtsmodel.Mention, mentionIDs []string) ([]model.Mention, error) { +	var errs multiError + +	if len(mentions) == 0 { +		var err error + +		// GTS model mentions were not populated +		// +		// Fetch GTS models for mention IDs +		mentions, err = c.db.GetMentions(ctx, mentionIDs) +		if err != nil { +			errs.Appendf("error fetching mentions from database: %v", err) +		} +	} + +	// Preallocate expected frontend slice +	apiMentions := make([]model.Mention, 0, len(mentions)) + +	// Convert GTS models to frontend models +	for _, mention := range mentions { +		apiMention, err := c.MentionToAPIMention(ctx, mention) +		if err != nil { +			errs.Appendf("error converting mention %s to api mention: %v", mention.ID, err) +			continue +		} +		apiMentions = append(apiMentions, apiMention) +	} + +	return apiMentions, errs.Combine() +} + +// convertTagsToAPITags will convert a slice of GTS model tags to frontend API model tags, falling back to IDs if no GTS models supplied. +func (c *converter) convertTagsToAPITags(ctx context.Context, tags []*gtsmodel.Tag, tagIDs []string) ([]model.Tag, error) { +	var errs multiError + +	if len(tags) == 0 { +		// GTS model tags were not populated + +		// Preallocate expected GTS slice +		tags = make([]*gtsmodel.Tag, 0, len(tagIDs)) + +		// Fetch GTS models for tag IDs +		for _, id := range tagIDs { +			tag := new(gtsmodel.Tag) +			if err := c.db.GetByID(ctx, id, tag); err != nil { +				errs.Appendf("error fetching tag %s from database: %v", id, err) +				continue +			} +			tags = append(tags, tag) +		} +	} + +	// Preallocate expected frontend slice +	apiTags := make([]model.Tag, 0, len(tags)) + +	// Convert GTS models to frontend models +	for _, tag := range tags { +		apiTag, err := c.TagToAPITag(ctx, tag) +		if err != nil { +			errs.Appendf("error converting tag %s to api tag: %v", tag.ID, err) +			continue +		} +		apiTags = append(apiTags, apiTag) +	} + +	return apiTags, errs.Combine() +} + +// multiError allows encapsulating multiple errors under a singular instance, +// which is useful when you only want to log on errors, not return early / bubble up. +// TODO: if this is useful elsewhere, move into a separate gts subpackage. +type multiError []string + +func (e *multiError) Append(err error) { +	*e = append(*e, err.Error()) +} + +func (e *multiError) Appendf(format string, args ...any) { +	*e = append(*e, fmt.Sprintf(format, args...)) +} + +// Combine converts this multiError to a singular error instance, returning nil if empty. +func (e multiError) Combine() error { +	if len(e) == 0 { +		return nil +	} +	return errors.New(`"` + strings.Join(e, `","`) + `"`) +} | 
