mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 07:52:24 -05:00
[feature] serdes for moved/also_known_as (#2600)
* [feature] serdes for moved/also_known_as * document `alsoKnownAs` and `movedTo` properties * only implicitly populate AKA uris from DB for local accounts * don't let remotes store more than 20 AKA uris to avoid shenanigans
This commit is contained in:
parent
3cc51d5072
commit
aa396c78d3
11 changed files with 392 additions and 22 deletions
|
|
@ -160,6 +160,8 @@ type Accountable interface {
|
|||
WithFollowing
|
||||
WithFollowers
|
||||
WithFeatured
|
||||
WithMovedTo
|
||||
WithAlsoKnownAs
|
||||
WithManuallyApprovesFollowers
|
||||
WithEndpoints
|
||||
WithTag
|
||||
|
|
@ -327,7 +329,7 @@ type TypeOrIRI interface {
|
|||
}
|
||||
|
||||
// Property represents the minimum interface for an ActivityStreams property with IRIs.
|
||||
type Property[T TypeOrIRI] interface {
|
||||
type Property[T WithIRI] interface {
|
||||
Len() int
|
||||
At(int) T
|
||||
|
||||
|
|
@ -441,6 +443,18 @@ type WithFeatured interface {
|
|||
SetTootFeatured(vocab.TootFeaturedProperty)
|
||||
}
|
||||
|
||||
// WithMovedTo represents an Object with ActivityStreamsMovedToProperty.
|
||||
type WithMovedTo interface {
|
||||
GetActivityStreamsMovedTo() vocab.ActivityStreamsMovedToProperty
|
||||
SetActivityStreamsMovedTo(vocab.ActivityStreamsMovedToProperty)
|
||||
}
|
||||
|
||||
// WithAlsoKnownAs represents an Object with ActivityStreamsAlsoKnownAsProperty.
|
||||
type WithAlsoKnownAs interface {
|
||||
GetActivityStreamsAlsoKnownAs() vocab.ActivityStreamsAlsoKnownAsProperty
|
||||
SetActivityStreamsAlsoKnownAs(vocab.ActivityStreamsAlsoKnownAsProperty)
|
||||
}
|
||||
|
||||
// WithAttributedTo represents an activity with ActivityStreamsAttributedToProperty
|
||||
type WithAttributedTo interface {
|
||||
GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty
|
||||
|
|
@ -551,6 +565,12 @@ type WithObject interface {
|
|||
SetActivityStreamsObject(vocab.ActivityStreamsObjectProperty)
|
||||
}
|
||||
|
||||
// WithTarget represents an activity with ActivityStreamsTargetProperty
|
||||
type WithTarget interface {
|
||||
GetActivityStreamsTarget() vocab.ActivityStreamsTargetProperty
|
||||
SetActivityStreamsTarget(vocab.ActivityStreamsTargetProperty)
|
||||
}
|
||||
|
||||
// WithNext represents an activity with ActivityStreamsNextProperty
|
||||
type WithNext interface {
|
||||
GetActivityStreamsNext() vocab.ActivityStreamsNextProperty
|
||||
|
|
|
|||
|
|
@ -391,6 +391,36 @@ func NormalizeOutgoingAttachmentProp(item WithAttachment, rawJSON map[string]int
|
|||
rawJSON["attachment"] = []interface{}{attachment}
|
||||
}
|
||||
|
||||
// NormalizeOutgoingAlsoKnownAsProp replaces single-entry alsoKnownAs values with
|
||||
// single-entry arrays, for better compatibility with other AP implementations.
|
||||
//
|
||||
// Ie:
|
||||
//
|
||||
// "alsoKnownAs": "https://example.org/users/some_user"
|
||||
//
|
||||
// becomes:
|
||||
//
|
||||
// "alsoKnownAs": ["https://example.org/users/some_user"]
|
||||
//
|
||||
// Noop for items with no attachments, or with attachments that are already a slice.
|
||||
func NormalizeOutgoingAlsoKnownAsProp(item WithAlsoKnownAs, rawJSON map[string]interface{}) {
|
||||
alsoKnownAs, ok := rawJSON["alsoKnownAs"]
|
||||
if !ok {
|
||||
// No 'alsoKnownAs',
|
||||
// nothing to change.
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := alsoKnownAs.([]interface{}); ok {
|
||||
// Already slice,
|
||||
// nothing to change.
|
||||
return
|
||||
}
|
||||
|
||||
// Coerce single-object to slice.
|
||||
rawJSON["alsoKnownAs"] = []interface{}{alsoKnownAs}
|
||||
}
|
||||
|
||||
// NormalizeOutgoingContentProp normalizes go-fed's funky formatting of content and
|
||||
// contentMap properties to a format better understood by other AP implementations.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ func AppendTo(with WithTo, to ...*url.URL) {
|
|||
// GetCc returns the IRIs contained in the Cc property of 'with'. Panics on entries with missing ID.
|
||||
func GetCc(with WithCc) []*url.URL {
|
||||
ccProp := with.GetActivityStreamsCc()
|
||||
return getIRIs[vocab.ActivityStreamsCcPropertyIterator](ccProp)
|
||||
return extractIRIs[vocab.ActivityStreamsCcPropertyIterator](ccProp)
|
||||
}
|
||||
|
||||
// AppendCc appends the given IRIs to the Cc property of 'with'.
|
||||
|
|
@ -120,7 +120,7 @@ func AppendCc(with WithCc, cc ...*url.URL) {
|
|||
// GetBcc returns the IRIs contained in the Bcc property of 'with'. Panics on entries with missing ID.
|
||||
func GetBcc(with WithBcc) []*url.URL {
|
||||
bccProp := with.GetActivityStreamsBcc()
|
||||
return getIRIs[vocab.ActivityStreamsBccPropertyIterator](bccProp)
|
||||
return extractIRIs[vocab.ActivityStreamsBccPropertyIterator](bccProp)
|
||||
}
|
||||
|
||||
// AppendBcc appends the given IRIs to the Bcc property of 'with'.
|
||||
|
|
@ -170,7 +170,7 @@ func AppendURL(with WithURL, url ...*url.URL) {
|
|||
// GetActorIRIs returns the IRIs contained in the Actor property of 'with'.
|
||||
func GetActorIRIs(with WithActor) []*url.URL {
|
||||
actorProp := with.GetActivityStreamsActor()
|
||||
return getIRIs[vocab.ActivityStreamsActorPropertyIterator](actorProp)
|
||||
return extractIRIs[vocab.ActivityStreamsActorPropertyIterator](actorProp)
|
||||
}
|
||||
|
||||
// AppendActorIRIs appends the given IRIs to the Actor property of 'with'.
|
||||
|
|
@ -188,7 +188,7 @@ func AppendActorIRIs(with WithActor, actor ...*url.URL) {
|
|||
// GetObjectIRIs returns the IRIs contained in the Object property of 'with'.
|
||||
func GetObjectIRIs(with WithObject) []*url.URL {
|
||||
objectProp := with.GetActivityStreamsObject()
|
||||
return getIRIs[vocab.ActivityStreamsObjectPropertyIterator](objectProp)
|
||||
return extractIRIs[vocab.ActivityStreamsObjectPropertyIterator](objectProp)
|
||||
}
|
||||
|
||||
// AppendObjectIRIs appends the given IRIs to the Object property of 'with'.
|
||||
|
|
@ -203,10 +203,28 @@ func AppendObjectIRIs(with WithObject) {
|
|||
})
|
||||
}
|
||||
|
||||
// GetTargetIRIs returns the IRIs contained in the Target property of 'with'.
|
||||
func GetTargetIRIs(with WithTarget) []*url.URL {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
return extractIRIs[vocab.ActivityStreamsTargetPropertyIterator](targetProp)
|
||||
}
|
||||
|
||||
// AppendTargetIRIs appends the given IRIs to the Target property of 'with'.
|
||||
func AppendTargetIRIs(with WithTarget) {
|
||||
appendIRIs(func() Property[vocab.ActivityStreamsTargetPropertyIterator] {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
if targetProp == nil {
|
||||
targetProp = streams.NewActivityStreamsTargetProperty()
|
||||
with.SetActivityStreamsTarget(targetProp)
|
||||
}
|
||||
return targetProp
|
||||
})
|
||||
}
|
||||
|
||||
// GetAttributedTo returns the IRIs contained in the AttributedTo property of 'with'.
|
||||
func GetAttributedTo(with WithAttributedTo) []*url.URL {
|
||||
attribProp := with.GetActivityStreamsAttributedTo()
|
||||
return getIRIs[vocab.ActivityStreamsAttributedToPropertyIterator](attribProp)
|
||||
return extractIRIs[vocab.ActivityStreamsAttributedToPropertyIterator](attribProp)
|
||||
}
|
||||
|
||||
// AppendAttributedTo appends the given IRIs to the AttributedTo property of 'with'.
|
||||
|
|
@ -224,7 +242,7 @@ func AppendAttributedTo(with WithAttributedTo, attribTo ...*url.URL) {
|
|||
// GetInReplyTo returns the IRIs contained in the InReplyTo property of 'with'.
|
||||
func GetInReplyTo(with WithInReplyTo) []*url.URL {
|
||||
replyProp := with.GetActivityStreamsInReplyTo()
|
||||
return getIRIs[vocab.ActivityStreamsInReplyToPropertyIterator](replyProp)
|
||||
return extractIRIs[vocab.ActivityStreamsInReplyToPropertyIterator](replyProp)
|
||||
}
|
||||
|
||||
// AppendInReplyTo appends the given IRIs to the InReplyTo property of 'with'.
|
||||
|
|
@ -334,6 +352,43 @@ func SetFeatured(with WithFeatured, featured *url.URL) {
|
|||
featuredProp.SetIRI(featured)
|
||||
}
|
||||
|
||||
// GetMovedTo returns the IRI contained in the movedTo property of 'with'.
|
||||
func GetMovedTo(with WithMovedTo) *url.URL {
|
||||
movedToProp := with.GetActivityStreamsMovedTo()
|
||||
if movedToProp == nil || !movedToProp.IsIRI() {
|
||||
return nil
|
||||
}
|
||||
return movedToProp.GetIRI()
|
||||
}
|
||||
|
||||
// SetMovedTo sets the given IRI on the movedTo property of 'with'.
|
||||
func SetMovedTo(with WithMovedTo, movedTo *url.URL) {
|
||||
movedToProp := with.GetActivityStreamsMovedTo()
|
||||
if movedToProp == nil {
|
||||
movedToProp = streams.NewActivityStreamsMovedToProperty()
|
||||
with.SetActivityStreamsMovedTo(movedToProp)
|
||||
}
|
||||
movedToProp.SetIRI(movedTo)
|
||||
}
|
||||
|
||||
// GetAlsoKnownAs returns the IRI contained in the alsoKnownAs property of 'with'.
|
||||
func GetAlsoKnownAs(with WithAlsoKnownAs) []*url.URL {
|
||||
alsoKnownAsProp := with.GetActivityStreamsAlsoKnownAs()
|
||||
return getIRIs[vocab.ActivityStreamsAlsoKnownAsPropertyIterator](alsoKnownAsProp)
|
||||
}
|
||||
|
||||
// SetAlsoKnownAs sets the given IRIs on the alsoKnownAs property of 'with'.
|
||||
func SetAlsoKnownAs(with WithAlsoKnownAs, alsoKnownAs []*url.URL) {
|
||||
appendIRIs(func() Property[vocab.ActivityStreamsAlsoKnownAsPropertyIterator] {
|
||||
alsoKnownAsProp := with.GetActivityStreamsAlsoKnownAs()
|
||||
if alsoKnownAsProp == nil {
|
||||
alsoKnownAsProp = streams.NewActivityStreamsAlsoKnownAsProperty()
|
||||
with.SetActivityStreamsAlsoKnownAs(alsoKnownAsProp)
|
||||
}
|
||||
return alsoKnownAsProp
|
||||
}, alsoKnownAs...)
|
||||
}
|
||||
|
||||
// GetPublished returns the time contained in the Published property of 'with'.
|
||||
func GetPublished(with WithPublished) time.Time {
|
||||
publishProp := with.GetActivityStreamsPublished()
|
||||
|
|
@ -465,7 +520,12 @@ func SetManuallyApprovesFollowers(with WithManuallyApprovesFollowers, manuallyAp
|
|||
mafProp.Set(manuallyApprovesFollowers)
|
||||
}
|
||||
|
||||
func getIRIs[T TypeOrIRI](prop Property[T]) []*url.URL {
|
||||
// extractIRIs extracts just the AP IRIs from an iterable
|
||||
// property that may contain types (with IRIs) or just IRIs.
|
||||
//
|
||||
// If you know the property contains only IRIs and no types,
|
||||
// then use getIRIs instead, since it's slightly faster.
|
||||
func extractIRIs[T TypeOrIRI](prop Property[T]) []*url.URL {
|
||||
if prop == nil || prop.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -490,7 +550,29 @@ func getIRIs[T TypeOrIRI](prop Property[T]) []*url.URL {
|
|||
return ids
|
||||
}
|
||||
|
||||
func appendIRIs[T TypeOrIRI](getProp func() Property[T], iri ...*url.URL) {
|
||||
// getIRIs gets AP IRIs from an iterable property of IRIs.
|
||||
//
|
||||
// Types will be ignored; to extract IRIs from an iterable
|
||||
// that may contain types too, use extractIRIs.
|
||||
func getIRIs[T WithIRI](prop Property[T]) []*url.URL {
|
||||
if prop == nil || prop.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
ids := make([]*url.URL, 0, prop.Len())
|
||||
for i := 0; i < prop.Len(); i++ {
|
||||
at := prop.At(i)
|
||||
if at.IsIRI() {
|
||||
id := at.GetIRI()
|
||||
if id != nil {
|
||||
ids = append(ids, id)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func appendIRIs[T WithIRI](getProp func() Property[T], iri ...*url.URL) {
|
||||
if len(iri) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,13 +90,12 @@ func serializeWithOrderedItems(t vocab.Type) (map[string]interface{}, error) {
|
|||
}
|
||||
|
||||
// SerializeAccountable is a custom serializer for any Accountable type.
|
||||
// This serializer rewrites the 'attachment' value of the Accountable, if
|
||||
// present, to always be an array/slice.
|
||||
// This serializer rewrites certain values of the Accountable, if present,
|
||||
// to always be an array/slice.
|
||||
//
|
||||
// While this is not strictly necessary in json-ld terms, most other fedi
|
||||
// implementations look for attachment to be an array of PropertyValue (field)
|
||||
// entries, and will not parse single-entry, non-array attachments on accounts
|
||||
// properly.
|
||||
// While this may not always be strictly necessary in json-ld terms, most other
|
||||
// fedi implementations look for certain fields to be an array and will not parse
|
||||
// single-entry, non-array fields on accounts properly.
|
||||
//
|
||||
// If the accountable is being serialized as a top-level object (eg., for serving
|
||||
// in response to an account dereference request), then includeContext should be
|
||||
|
|
@ -126,6 +125,7 @@ func serializeAccountable(t vocab.Type, includeContext bool) (map[string]interfa
|
|||
}
|
||||
|
||||
NormalizeOutgoingAttachmentProp(accountable, data)
|
||||
NormalizeOutgoingAlsoKnownAsProp(accountable, data)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue