Better abstract programmatic sending of activities.

このコミットが含まれているのは:
Cory Slep 2019-03-26 22:00:55 +01:00
コミット a65388c27b
2個のファイルの変更87行の追加52行の削除

ファイルの表示

@ -1,6 +1,7 @@
package pub package pub
import ( import (
"github.com/go-fed/activity/streams/vocab"
"context" "context"
"net/http" "net/http"
"net/url" "net/url"
@ -96,13 +97,18 @@ type Actor interface {
// Activity to a federating peer. // Activity to a federating peer.
type FederatingActor interface { type FederatingActor interface {
Actor Actor
// Deliver sends a federated message. // Send a federated activity.
// //
// The provided url must be the outbox of the sender for identity // The provided url must be the outbox of the sender. All processing of
// purposes only. It is up to the caller to ensure that the provided // the activity occurs similarly to the C2S flow:
// activity has been added to the outbox. // - If t is not an Activity, it is wrapped in a Create activity.
// - A new ID is generated for the activity
// - The activity is added to the specified outbox
// - The activity is prepared and delivered to recipients
// //
// Note that this function will only behave as expected if the // Note that this function will only behave as expected if the
// implementation has been constructed to support federation. // implementation has been constructed to support federation. This
Deliver(c context.Context, outbox *url.URL, activity Activity) error // method will guaranteed work for non-custom Actors. For custom actors,
// care should be used to not call this method if only C2S is supported.
Send(c context.Context, outbox *url.URL, t vocab.Type) (Activity, error)
} }

ファイルの表示

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -329,50 +330,18 @@ func (b *baseActor) PostOutbox(c context.Context, w http.ResponseWriter, r *http
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return true, nil return true, nil
} }
// If the value is not an Activity or type extending from Activity, then // The HTTP request steps are complete, complete the rest of the outbox
// we need to wrap it in a Create Activity. // and delivery process.
if !streams.IsOrExtendsActivityStreamsActivity(asValue) { activity, err := b.deliver(c, r.URL, asValue, m)
asValue, err = b.delegate.WrapInCreate(c, asValue, r.URL) // Special case: We know it is a bad request if the object or
if err != nil { // target properties needed to be populated, but weren't.
return true, err
}
}
// At this point, this should be a safe conversion. If this error is
// triggered, then there is either a bug in the delegation of
// WrapInCreate, behavior is not lining up in the generated ExtendedBy
// code, or something else is incorrect with the type system.
activity, ok := asValue.(Activity)
if !ok {
return true, fmt.Errorf("activity streams value is not an Activity: %T", asValue)
}
// Delegate generating new IDs for the activity and all new objects.
if err = b.delegate.AddNewIds(c, activity); err != nil {
return true, err
}
// Post the activity to the actor's outbox and trigger side effects for
// that particular Activity type.
deliverable, err := b.delegate.PostOutbox(c, activity, r.URL, m)
if err != nil {
// Special case: We know it is a bad request if the object or
// target properties needed to be populated, but weren't.
//
// Send the rejection to the peer.
if err == ErrObjectRequired || err == ErrTargetRequired {
w.WriteHeader(http.StatusBadRequest)
return true, nil
}
return true, err
}
// Request has been processed and all side effects internal to this
// application server have finished. Begin side effects affecting other
// servers and/or the client who sent this request.
// //
// If we are federating and the type is a deliverable one, then deliver // Send the rejection to the client.
// the activity to federating peers. if err == ErrObjectRequired || err == ErrTargetRequired {
if b.enableFederatedProtocol && deliverable { w.WriteHeader(http.StatusBadRequest)
if err := b.delegate.Deliver(c, r.URL, activity); err != nil { return true, nil
return true, err } else if err != nil {
} return true, err
} }
// Respond to the request with the new Activity's IRI location. // Respond to the request with the new Activity's IRI location.
w.Header().Set(locationHeader, activity.GetActivityStreamsId().Get().String()) w.Header().Set(locationHeader, activity.GetActivityStreamsId().Get().String())
@ -423,7 +392,67 @@ func (b *baseActor) GetOutbox(c context.Context, w http.ResponseWriter, r *http.
return true, nil return true, nil
} }
// Deliver delegates directly to the delegate actor. // deliver delegates all outbox handling steps and optionally will federate the
func (b *baseActorFederating) Deliver(c context.Context, outbox *url.URL, activity Activity) error { // activity if the federated protocol is enabled.
return b.delegate.Deliver(c, outbox, activity) //
// This function is not exported so an Actor that only supports C2S cannot be
// type casted to a FederatingActor. It doesn't exactly fit the Send method
// signature anyways.
//
// Note: 'm' is nilable.
func (b *baseActor) deliver(c context.Context, outbox *url.URL, asValue vocab.Type, m map[string]interface{}) (activity Activity, err error) {
// If the value is not an Activity or type extending from Activity, then
// we need to wrap it in a Create Activity.
if !streams.IsOrExtendsActivityStreamsActivity(asValue) {
asValue, err = b.delegate.WrapInCreate(c, asValue, outbox)
if err != nil {
return
}
}
// At this point, this should be a safe conversion. If this error is
// triggered, then there is either a bug in the delegation of
// WrapInCreate, behavior is not lining up in the generated ExtendedBy
// code, or something else is incorrect with the type system.
var ok bool
activity, ok = asValue.(Activity)
if !ok {
err = fmt.Errorf("activity streams value is not an Activity: %T", asValue)
return
}
// Delegate generating new IDs for the activity and all new objects.
if err = b.delegate.AddNewIds(c, activity); err != nil {
return
}
// Post the activity to the actor's outbox and trigger side effects for
// that particular Activity type.
//
// Since 'm' is nil-able and side effects may need access to literal nil
// values, such as for Update activities, ensure 'm' is non-nil.
if m == nil {
m, err = asValue.Serialize()
if err != nil {
return
}
}
deliverable, err := b.delegate.PostOutbox(c, activity, outbox, m)
if err != nil {
return
}
// Request has been processed and all side effects internal to this
// application server have finished. Begin side effects affecting other
// servers and/or the client who sent this request.
//
// If we are federating and the type is a deliverable one, then deliver
// the activity to federating peers.
if b.enableFederatedProtocol && deliverable {
if err = b.delegate.Deliver(c, outbox, activity); err != nil {
return
}
}
return
}
// Send is programmatically accessible if the federated protocol is enabled.
func (b *baseActorFederating) Send(c context.Context, outbox *url.URL, t vocab.Type) (Activity, error) {
return b.deliver(c, outbox, t, nil)
} }