Implement client Create handler
このコミットが含まれているのは:
コミット
ea1a387866
153
pub/fed.go
153
pub/fed.go
|
@ -2,6 +2,7 @@ package pub
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-fed/activity/streams"
|
||||
"github.com/go-fed/activity/vocab"
|
||||
|
@ -10,6 +11,13 @@ import (
|
|||
"net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrObjectRequired means the activity needs its object property set.
|
||||
ErrObjectRequired = errors.New("object property required")
|
||||
// ErrTypeRequired means the activity needs its type property set.
|
||||
ErrTypeRequired = errors.New("type property required")
|
||||
)
|
||||
|
||||
// Object is the interface for ActivityPub compliant ActivityStream types.
|
||||
//
|
||||
// ActivityPub obects must have 'id' and 'type' properties to be spec compliant.
|
||||
|
@ -188,16 +196,29 @@ type Receiver interface {
|
|||
Undo(id string, s *streams.Undo) error
|
||||
}
|
||||
|
||||
// ClientReceiver is provided by users of this library and designed to handle
|
||||
// receiving messaged from ActivityPub clients through the Social API.
|
||||
type ClientReceiver interface {
|
||||
// Create requires the client application to persist the 'object' that
|
||||
// was created.
|
||||
Create(id string, s *streams.Create) error
|
||||
}
|
||||
|
||||
type federator struct {
|
||||
// App is the client application that is ActivityPub aware.
|
||||
//
|
||||
// It is always required.
|
||||
App Application
|
||||
// Receiver provides callbacks when handling incoming messages received
|
||||
// via the Federated Protocol.
|
||||
// via the Federated Protocol, or server-to-server communications.
|
||||
//
|
||||
// It is only required if EnableServer is true.
|
||||
Receiver Receiver
|
||||
// ClientReceiver provides callbacks when handling incoming messages
|
||||
// received via the Social API, or client-to-server communications.
|
||||
//
|
||||
// It is only required if EnableClient is true.
|
||||
ClientReceiver ClientReceiver
|
||||
// Client is used to federate with other ActivityPub servers.
|
||||
//
|
||||
// It is only required if EnableServer is true.
|
||||
|
@ -301,6 +322,13 @@ func (f *federator) GetInbox(id string) HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// PostOutpox provides a HTTP handler for ActivityPub requests for the given id
|
||||
// token. The client ID token is passed forwards to other interfaces for
|
||||
// application specific behavior. The handler will return true if it handled
|
||||
// the request as an ActivityPub request. If it returns an error, it is up to
|
||||
// the client to determine how to respond via HTTP.
|
||||
//
|
||||
// Note that the error could be ErrObjectRequired or ErrTypeRequired.
|
||||
func (f *federator) PostOutbox(id string) HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) (bool, error) {
|
||||
if !isActivityPubPost(r) {
|
||||
|
@ -330,7 +358,6 @@ func (f *federator) PostOutbox(id string) HandlerFunc {
|
|||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
newId := f.App.NewId(id, typer)
|
||||
if !isActivityType(typer) {
|
||||
actorIri, err := f.App.ActorIRI(id)
|
||||
if err != nil {
|
||||
|
@ -342,6 +369,7 @@ func (f *federator) PostOutbox(id string) HandlerFunc {
|
|||
}
|
||||
typer = f.wrapInCreate(obj, actorIri)
|
||||
}
|
||||
newId := f.App.NewId(id, typer)
|
||||
typer.SetId(newId)
|
||||
if m, err = typer.Serialize(); err != nil {
|
||||
return true, err
|
||||
|
@ -434,15 +462,96 @@ func (f *federator) getPostOutboxResolver(id string) *streams.Resolver {
|
|||
|
||||
func (f *federator) handleClientCreate(id string) func(s *streams.Create) error {
|
||||
return func(s *streams.Create) error {
|
||||
// TODO: Enforce object
|
||||
// TODO: Implement
|
||||
return nil
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
c := s.Raw()
|
||||
// When a Create activity is posted, the actor of the activity
|
||||
// SHOULD be copied onto the object's attributedTo field.
|
||||
// Presumably only if it doesn't already exist, to prevent
|
||||
// duplicate deliveries.
|
||||
createActorIds := make(map[string]interface{})
|
||||
for i := 0; i < c.ActorLen(); i++ {
|
||||
if c.IsActorObject(i) {
|
||||
obj := c.GetActorObject(i)
|
||||
id := obj.GetId()
|
||||
createActorIds[(&id).String()] = obj
|
||||
} else if c.IsActorLink(i) {
|
||||
l := c.GetActorLink(i)
|
||||
href := l.GetHref()
|
||||
createActorIds[(&href).String()] = l
|
||||
} else if c.IsActorIRI(i) {
|
||||
iri := c.GetActorIRI(i)
|
||||
createActorIds[(&iri).String()] = iri
|
||||
}
|
||||
}
|
||||
var obj []vocab.ObjectType
|
||||
for i := 0; i < c.ObjectLen(); i++ {
|
||||
if c.IsObject(i) {
|
||||
obj = append(obj, c.GetObject(i))
|
||||
} else if c.IsObjectIRI(i) {
|
||||
return fmt.Errorf("unsupported: Create Activity with 'object' that is only an IRI")
|
||||
}
|
||||
}
|
||||
objectAttributedToIds := make([]map[string]interface{}, len(obj))
|
||||
for i := range objectAttributedToIds {
|
||||
objectAttributedToIds[i] = make(map[string]interface{})
|
||||
}
|
||||
for k, o := range obj {
|
||||
for i := 0; i < o.AttributedToLen(); i++ {
|
||||
if o.IsAttributedToObject(i) {
|
||||
at := o.GetAttributedToObject(i)
|
||||
id := o.GetId()
|
||||
objectAttributedToIds[k][(&id).String()] = at
|
||||
} else if o.IsAttributedToLink(i) {
|
||||
at := o.GetAttributedToLink(i)
|
||||
href := at.GetHref()
|
||||
objectAttributedToIds[k][(&href).String()] = at
|
||||
} else if o.IsAttributedToIRI(i) {
|
||||
iri := o.GetAttributedToIRI(i)
|
||||
objectAttributedToIds[k][(&iri).String()] = iri
|
||||
}
|
||||
}
|
||||
}
|
||||
for k, v := range createActorIds {
|
||||
for i, attributedToMap := range objectAttributedToIds {
|
||||
if _, ok := attributedToMap[k]; !ok {
|
||||
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||
obj[i].AddAttributedToObject(vObj)
|
||||
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||
obj[i].AddAttributedToLink(vLink)
|
||||
} else if vIRI, ok := v.(url.URL); ok {
|
||||
obj[i].AddAttributedToIRI(vIRI)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// As such, a server SHOULD copy any recipients of the Create activity to its
|
||||
// object upon initial distribution, and likewise with copying recipients from
|
||||
// the object to the wrapping Create activity.
|
||||
// Again, presumably if it does not already exist.
|
||||
for _, attributedToMap := range objectAttributedToIds {
|
||||
for k, v := range attributedToMap {
|
||||
if _, ok := createActorIds[k]; !ok {
|
||||
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||
c.AddActorObject(vObj)
|
||||
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||
c.AddActorLink(vLink)
|
||||
} else if vIRI, ok := v.(url.URL); ok {
|
||||
c.AddActorIRI(vIRI)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return f.ClientReceiver.Create(id, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *federator) handleClientUpdate(id string) func(s *streams.Update) error {
|
||||
return func(s *streams.Update) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -450,7 +559,9 @@ func (f *federator) handleClientUpdate(id string) func(s *streams.Update) error
|
|||
|
||||
func (f *federator) handleClientDelete(id string) func(s *streams.Delete) error {
|
||||
return func(s *streams.Delete) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -458,7 +569,9 @@ func (f *federator) handleClientDelete(id string) func(s *streams.Delete) error
|
|||
|
||||
func (f *federator) handleClientFollow(id string) func(s *streams.Follow) error {
|
||||
return func(s *streams.Follow) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -480,7 +593,11 @@ func (f *federator) handleClientReject(id string) func(s *streams.Reject) error
|
|||
|
||||
func (f *federator) handleClientAdd(id string) func(s *streams.Add) error {
|
||||
return func(s *streams.Add) error {
|
||||
// TODO: Enforce object & target
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
} else if s.LenType() == 0 {
|
||||
return ErrTypeRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -488,7 +605,11 @@ func (f *federator) handleClientAdd(id string) func(s *streams.Add) error {
|
|||
|
||||
func (f *federator) handleClientRemove(id string) func(s *streams.Remove) error {
|
||||
return func(s *streams.Remove) error {
|
||||
// TODO: Enforce object & target
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
} else if s.LenType() == 0 {
|
||||
return ErrTypeRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -496,7 +617,9 @@ func (f *federator) handleClientRemove(id string) func(s *streams.Remove) error
|
|||
|
||||
func (f *federator) handleClientLike(id string) func(s *streams.Like) error {
|
||||
return func(s *streams.Like) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -504,7 +627,9 @@ func (f *federator) handleClientLike(id string) func(s *streams.Like) error {
|
|||
|
||||
func (f *federator) handleClientUndo(id string) func(s *streams.Undo) error {
|
||||
return func(s *streams.Undo) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
@ -512,7 +637,9 @@ func (f *federator) handleClientUndo(id string) func(s *streams.Undo) error {
|
|||
|
||||
func (f *federator) handleClientBlock(id string) func(s *streams.Block) error {
|
||||
return func(s *streams.Block) error {
|
||||
// TODO: Enforce object
|
||||
if s.LenObject() == 0 {
|
||||
return ErrObjectRequired
|
||||
}
|
||||
// TODO: Implement
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-fed/activity/streams"
|
||||
"github.com/go-fed/activity/vocab"
|
||||
"net/url"
|
||||
|
@ -72,18 +73,29 @@ func toOrderedCollectionPage(m map[string]interface{}) (c *streams.OrderedCollec
|
|||
return
|
||||
}
|
||||
|
||||
func toTypeIder(m map[string]interface{}) (t typeIder, err error) {
|
||||
func toTypeIder(m map[string]interface{}) (tid typeIder, err error) {
|
||||
var t []typeIder
|
||||
r := &streams.Resolver{
|
||||
AnyObjectCallback: func(i vocab.ObjectType) error {
|
||||
t = i
|
||||
t = append(t, i)
|
||||
return nil
|
||||
},
|
||||
AnyLinkCallback: func(i vocab.LinkType) error {
|
||||
t = i
|
||||
t = append(t, i)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
err = r.Deserialize(m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// TODO: Support more than one, which will enable creating multiple
|
||||
// objects simultaneously.
|
||||
if len(t) != 1 {
|
||||
err = fmt.Errorf("too many object/links: %d", len(t))
|
||||
return
|
||||
}
|
||||
tid = t[0]
|
||||
return
|
||||
}
|
||||
|
||||
|
|
読み込み中…
新しいイシューから参照