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
import (
"github.com/go-fed/activity/streams/vocab"
"context"
"net/http"
"net/url"
@ -96,13 +97,18 @@ type Actor interface {
// Activity to a federating peer.
type FederatingActor interface {
Actor
// Deliver sends a federated message.
// Send a federated activity.
//
// The provided url must be the outbox of the sender for identity
// purposes only. It is up to the caller to ensure that the provided
// activity has been added to the outbox.
// The provided url must be the outbox of the sender. All processing of
// the activity occurs similarly to the C2S flow:
// - 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
// implementation has been constructed to support federation.
Deliver(c context.Context, outbox *url.URL, activity Activity) error
// implementation has been constructed to support federation. This
// 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"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"io/ioutil"
"net/http"
"net/url"
@ -329,50 +330,18 @@ func (b *baseActor) PostOutbox(c context.Context, w http.ResponseWriter, r *http
w.WriteHeader(http.StatusBadRequest)
return true, nil
}
// 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, r.URL)
if err != nil {
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.
// The HTTP request steps are complete, complete the rest of the outbox
// and delivery process.
activity, err := b.deliver(c, r.URL, asValue, m)
// Special case: We know it is a bad request if the object or
// target properties needed to be populated, but weren't.
//
// 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, r.URL, activity); err != nil {
return true, err
}
// Send the rejection to the client.
if err == ErrObjectRequired || err == ErrTargetRequired {
w.WriteHeader(http.StatusBadRequest)
return true, nil
} else if err != nil {
return true, err
}
// Respond to the request with the new Activity's IRI location.
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
}
// Deliver delegates directly to the delegate actor.
func (b *baseActorFederating) Deliver(c context.Context, outbox *url.URL, activity Activity) error {
return b.delegate.Deliver(c, outbox, activity)
// deliver delegates all outbox handling steps and optionally will federate the
// activity if the federated protocol is enabled.
//
// 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)
}