[feature] tentatively start adding polls support (#2249)

This commit is contained in:
kim 2023-10-04 13:09:42 +01:00 committed by GitHub
commit c6e00afc7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 657 additions and 393 deletions

View file

@ -32,10 +32,10 @@ import (
func ToCollectionPageIterator(t vocab.Type) (CollectionPageIterator, error) {
switch name := t.GetTypeName(); name {
case ObjectCollectionPage:
t := t.(vocab.ActivityStreamsCollectionPage) //nolint:forcetypeassert
t := t.(vocab.ActivityStreamsCollectionPage)
return WrapCollectionPage(t), nil
case ObjectOrderedCollectionPage:
t := t.(vocab.ActivityStreamsOrderedCollectionPage) //nolint:forcetypeassert
t := t.(vocab.ActivityStreamsOrderedCollectionPage)
return WrapOrderedCollectionPage(t), nil
default:
return nil, fmt.Errorf("%T(%s) was not CollectionPage-like", t, name)
@ -74,7 +74,7 @@ func (iter *regularCollectionPageIterator) PrevPage() WithIRI {
return iter.GetActivityStreamsPrev()
}
func (iter *regularCollectionPageIterator) NextItem() IteratorItemable {
func (iter *regularCollectionPageIterator) NextItem() TypeOrIRI {
if !iter.initItems() {
return nil
}
@ -83,7 +83,7 @@ func (iter *regularCollectionPageIterator) NextItem() IteratorItemable {
return cur
}
func (iter *regularCollectionPageIterator) PrevItem() IteratorItemable {
func (iter *regularCollectionPageIterator) PrevItem() TypeOrIRI {
if !iter.initItems() {
return nil
}
@ -130,7 +130,7 @@ func (iter *orderedCollectionPageIterator) PrevPage() WithIRI {
return iter.GetActivityStreamsPrev()
}
func (iter *orderedCollectionPageIterator) NextItem() IteratorItemable {
func (iter *orderedCollectionPageIterator) NextItem() TypeOrIRI {
if !iter.initItems() {
return nil
}
@ -139,7 +139,7 @@ func (iter *orderedCollectionPageIterator) NextItem() IteratorItemable {
return cur
}
func (iter *orderedCollectionPageIterator) PrevItem() IteratorItemable {
func (iter *orderedCollectionPageIterator) PrevItem() TypeOrIRI {
if !iter.initItems() {
return nil
}

View file

@ -35,39 +35,56 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// ExtractObject will extract an object vocab.Type from given implementing interface.
func ExtractObject(with WithObject) vocab.Type {
// ExtractObjects will extract object vocab.Types from given implementing interface.
func ExtractObjects(with WithObject) []TypeOrIRI {
// Extract the attached object (if any).
obj := with.GetActivityStreamsObject()
if obj == nil {
objProp := with.GetActivityStreamsObject()
if objProp == nil {
return nil
}
// Only support single
// objects (for now...)
if obj.Len() != 1 {
// Check for zero len.
if objProp.Len() == 0 {
return nil
}
// Extract object vocab.Type.
return obj.At(0).GetType()
// Accumulate all of the objects into a slice.
objs := make([]TypeOrIRI, objProp.Len())
for i := 0; i < objProp.Len(); i++ {
objs[i] = objProp.At(i)
}
return objs
}
// ExtractActivityData will extract the usable data type (e.g. Note, Question, etc) and corresponding JSON, from activity.
func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) (vocab.Type, map[string]any, bool) {
func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeOrIRI, []any, bool) {
switch typeName := activity.GetTypeName(); {
// Activity (has "object").
case isActivity(typeName):
objType := ExtractObject(activity)
if objType == nil {
objTypes := ExtractObjects(activity)
if len(objTypes) == 0 {
return nil, nil, false
}
objJSON, _ := rawJSON["object"].(map[string]any)
return objType, objJSON, true
var objJSON []any
switch json := rawJSON["object"].(type) {
case nil:
// do nothing
case map[string]any:
// Wrap map in slice.
objJSON = []any{json}
case []any:
// Use existing slice.
objJSON = json
}
return objTypes, objJSON, true
// IntransitiveAcitivity (no "object").
case isIntransitiveActivity(typeName):
return activity, rawJSON, false
asTypeOrIRI := _TypeOrIRI{activity} // wrap activity.
return []TypeOrIRI{&asTypeOrIRI}, []any{rawJSON}, true
// Unknown.
default:

View file

@ -247,14 +247,8 @@ type CollectionPageIterator interface {
NextPage() WithIRI
PrevPage() WithIRI
NextItem() IteratorItemable
PrevItem() IteratorItemable
}
// IteratorItemable represents the minimum interface for an item in an iterator.
type IteratorItemable interface {
WithIRI
WithType
NextItem() TypeOrIRI
PrevItem() TypeOrIRI
}
// Flaggable represents the minimum interface for an activitystreams 'Flag' activity.
@ -267,6 +261,12 @@ type Flaggable interface {
WithObject
}
// TypeOrIRI represents the minimum interface for something that may be a vocab.Type OR IRI.
type TypeOrIRI interface {
WithIRI
WithType
}
// WithJSONLDId represents an activity with JSONLDIdProperty.
type WithJSONLDId interface {
GetJSONLDId() vocab.JSONLDIdProperty

View file

@ -39,60 +39,48 @@ import (
// This function is a noop if the type passed in is anything except a Create or Update with a Statusable or Accountable as its Object.
func NormalizeIncomingActivity(activity pub.Activity, rawJSON map[string]interface{}) {
// From the activity extract the data vocab.Type + its "raw" JSON.
dataType, rawData, ok := ExtractActivityData(activity, rawJSON)
if !ok {
dataIfaces, rawData, ok := ExtractActivityData(activity, rawJSON)
if !ok || len(dataIfaces) != len(rawData) {
// non-equal lengths *shouldn't* happen,
// but this is just an integrity check.
return
}
switch dataType.GetTypeName() {
// "Pollable" types.
case ActivityQuestion:
pollable, ok := dataType.(Pollable)
if !ok {
return
// Iterate over the available data.
for i, dataIface := range dataIfaces {
// Try to get as vocab.Type, else
// skip this entry for normalization.
dataType := dataIface.GetType()
if dataType == nil {
continue
}
// Normalize the Pollable specific properties.
NormalizeIncomingPollOptions(pollable, rawData)
// Fallthrough to handle
// the rest as Statusable.
fallthrough
// "Statusable" types.
case ObjectArticle,
ObjectDocument,
ObjectImage,
ObjectVideo,
ObjectNote,
ObjectPage,
ObjectEvent,
ObjectPlace,
ObjectProfile:
statusable, ok := dataType.(Statusable)
// Get the raw data map at index, else skip
// this entry due to impossible normalization.
rawData, ok := rawData[i].(map[string]any)
if !ok {
return
continue
}
// Normalize everything we can on the statusable.
NormalizeIncomingContent(statusable, rawData)
NormalizeIncomingAttachments(statusable, rawData)
NormalizeIncomingSummary(statusable, rawData)
NormalizeIncomingName(statusable, rawData)
if statusable, ok := ToStatusable(dataType); ok {
if pollable, ok := ToPollable(dataType); ok {
// Normalize the Pollable specific properties.
NormalizeIncomingPollOptions(pollable, rawData)
}
// "Accountable" types.
case ActorApplication,
ActorGroup,
ActorOrganization,
ActorPerson,
ActorService:
accountable, ok := dataType.(Accountable)
if !ok {
return
// Normalize everything we can on the statusable.
NormalizeIncomingContent(statusable, rawData)
NormalizeIncomingAttachments(statusable, rawData)
NormalizeIncomingSummary(statusable, rawData)
NormalizeIncomingName(statusable, rawData)
continue
}
// Normalize everything we can on the accountable.
NormalizeIncomingSummary(accountable, rawData)
if accountable, ok := ToAccountable(dataType); ok {
// Normalize everything we can on the accountable.
NormalizeIncomingSummary(accountable, rawData)
continue
}
}
}

43
internal/ap/util.go Normal file
View file

@ -0,0 +1,43 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ap
import (
"net/url"
"github.com/superseriousbusiness/activity/streams/vocab"
)
// _TypeOrIRI wraps a vocab.Type to implement TypeOrIRI.
type _TypeOrIRI struct {
vocab.Type
}
func (t *_TypeOrIRI) GetType() vocab.Type {
return t.Type
}
func (t *_TypeOrIRI) GetIRI() *url.URL {
return nil
}
func (t *_TypeOrIRI) IsIRI() bool {
return false
}
func (t *_TypeOrIRI) SetIRI(*url.URL) {}