Add PostOutbox Create unit tests.
Fix normalizing the recipents between the object and the activity. Also allow the handlers to adjust what gets placed in the outbox, since the client-to-server API allows the server to modify the object before being delivered.
このコミットが含まれているのは:
コミット
2adcc56581
34
pub/fed.go
34
pub/fed.go
|
@ -271,11 +271,11 @@ func (f *federator) PostOutbox(c context.Context, w http.ResponseWriter, r *http
|
||||||
if m, err = typer.Serialize(); err != nil {
|
if m, err = typer.Serialize(); err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
if err := f.addToOutbox(c, r, m); err != nil {
|
deliverable := false
|
||||||
|
if err = f.getPostOutboxResolver(c, &deliverable, &m).Deserialize(m); err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
deliverable := false
|
if err := f.addToOutbox(c, r, m); err != nil {
|
||||||
if err = f.getPostOutboxResolver(c, &deliverable).Deserialize(m); err != nil {
|
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
if f.EnableServer && deliverable {
|
if f.EnableServer && deliverable {
|
||||||
|
@ -334,9 +334,9 @@ func (f *federator) addToOutbox(c context.Context, r *http.Request, m map[string
|
||||||
return f.App.Set(c, outbox)
|
return f.App.Set(c, outbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federator) getPostOutboxResolver(c context.Context, deliverable *bool) *streams.Resolver {
|
func (f *federator) getPostOutboxResolver(c context.Context, deliverable *bool, toAddToOutbox *map[string]interface{}) *streams.Resolver {
|
||||||
return &streams.Resolver{
|
return &streams.Resolver{
|
||||||
CreateCallback: f.handleClientCreate(c, deliverable),
|
CreateCallback: f.handleClientCreate(c, deliverable, toAddToOutbox),
|
||||||
UpdateCallback: f.handleClientUpdate(c, deliverable),
|
UpdateCallback: f.handleClientUpdate(c, deliverable),
|
||||||
DeleteCallback: f.handleClientDelete(c, deliverable),
|
DeleteCallback: f.handleClientDelete(c, deliverable),
|
||||||
FollowCallback: f.handleClientFollow(c, deliverable),
|
FollowCallback: f.handleClientFollow(c, deliverable),
|
||||||
|
@ -351,7 +351,7 @@ func (f *federator) getPostOutboxResolver(c context.Context, deliverable *bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool) func(s *streams.Create) error {
|
func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool, toAddToOutbox *map[string]interface{}) func(s *streams.Create) error {
|
||||||
return func(s *streams.Create) error {
|
return func(s *streams.Create) error {
|
||||||
*deliverable = true
|
*deliverable = true
|
||||||
if s.LenObject() == 0 {
|
if s.LenObject() == 0 {
|
||||||
|
@ -379,12 +379,11 @@ func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool) f
|
||||||
}
|
}
|
||||||
var obj []vocab.ObjectType
|
var obj []vocab.ObjectType
|
||||||
for i := 0; i < c.ObjectLen(); i++ {
|
for i := 0; i < c.ObjectLen(); i++ {
|
||||||
if c.IsObject(i) {
|
if !c.IsObject(i) {
|
||||||
obj = append(obj, c.GetObject(i))
|
|
||||||
} else if c.IsObjectIRI(i) {
|
|
||||||
// TODO: Fetch IRIs as well
|
// TODO: Fetch IRIs as well
|
||||||
return fmt.Errorf("unsupported: Create Activity with 'object' that is only an IRI")
|
return fmt.Errorf("unsupported: Create Activity with 'object' that is only an IRI")
|
||||||
}
|
}
|
||||||
|
obj = append(obj, c.GetObject(i))
|
||||||
}
|
}
|
||||||
objectAttributedToIds := make([]map[string]interface{}, len(obj))
|
objectAttributedToIds := make([]map[string]interface{}, len(obj))
|
||||||
for i := range objectAttributedToIds {
|
for i := range objectAttributedToIds {
|
||||||
|
@ -419,10 +418,6 @@ func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool) f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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 _, attributedToMap := range objectAttributedToIds {
|
||||||
for k, v := range attributedToMap {
|
for k, v := range attributedToMap {
|
||||||
if _, ok := createActorIds[k]; !ok {
|
if _, ok := createActorIds[k]; !ok {
|
||||||
|
@ -436,6 +431,12 @@ func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool) f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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.
|
||||||
|
if err := f.sameRecipients(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Create requires the client application to persist the 'object' that
|
// Create requires the client application to persist the 'object' that
|
||||||
// was created.
|
// was created.
|
||||||
for _, o := range obj {
|
for _, o := range obj {
|
||||||
|
@ -443,6 +444,13 @@ func (f *federator) handleClientCreate(ctx context.Context, deliverable *bool) f
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Persist the above changes in the outbox
|
||||||
|
var err error
|
||||||
|
*toAddToOutbox = make(map[string]interface{})
|
||||||
|
*toAddToOutbox, err = c.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return f.ClientCallbacker.Create(ctx, s)
|
return f.ClientCallbacker.Create(ctx, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
202
pub/fed_test.go
202
pub/fed_test.go
|
@ -34,34 +34,36 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
iri *url.URL
|
iri *url.URL
|
||||||
noteIRI *url.URL
|
noteIRI *url.URL
|
||||||
noteActivityIRI *url.URL
|
noteActivityIRI *url.URL
|
||||||
updateActivityIRI *url.URL
|
updateActivityIRI *url.URL
|
||||||
testNewIRI *url.URL
|
testNewIRI *url.URL
|
||||||
sallyIRI *url.URL
|
sallyIRI *url.URL
|
||||||
sallyIRIInbox *url.URL
|
sallyIRIInbox *url.URL
|
||||||
sallyActor *vocab.Person
|
sallyActor *vocab.Person
|
||||||
sallyActorJSON []byte
|
sallyActorJSON []byte
|
||||||
samIRI *url.URL
|
samIRI *url.URL
|
||||||
samIRIInbox *url.URL
|
samIRIInbox *url.URL
|
||||||
samIRIFollowers *url.URL
|
samIRIFollowers *url.URL
|
||||||
samActor *vocab.Person
|
samActor *vocab.Person
|
||||||
samActorJSON []byte
|
samActorJSON []byte
|
||||||
testNote *vocab.Note
|
testNote *vocab.Note
|
||||||
testSingleOrderedCollection *vocab.OrderedCollection
|
testSingleOrderedCollection *vocab.OrderedCollection
|
||||||
testCreateNote *vocab.Create
|
testCreateNote *vocab.Create
|
||||||
testUpdateNote *vocab.Update
|
testUpdateNote *vocab.Update
|
||||||
testDeleteNote *vocab.Delete
|
testDeleteNote *vocab.Delete
|
||||||
testTombstoneNote *vocab.Tombstone
|
testTombstoneNote *vocab.Tombstone
|
||||||
testFollow *vocab.Follow
|
testFollow *vocab.Follow
|
||||||
testAcceptNote *vocab.Accept
|
testAcceptNote *vocab.Accept
|
||||||
testAcceptFollow *vocab.Accept
|
testAcceptFollow *vocab.Accept
|
||||||
testRejectFollow *vocab.Reject
|
testRejectFollow *vocab.Reject
|
||||||
testAddNote *vocab.Add
|
testAddNote *vocab.Add
|
||||||
testRemoveNote *vocab.Remove
|
testRemoveNote *vocab.Remove
|
||||||
testLikeNote *vocab.Like
|
testLikeNote *vocab.Like
|
||||||
testUndoLike *vocab.Undo
|
testUndoLike *vocab.Undo
|
||||||
|
testClientExpectedNote *vocab.Note
|
||||||
|
testClientExpectedCreateNote *vocab.Create
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -207,6 +209,19 @@ func init() {
|
||||||
testUndoLike.AddActorObject(sallyActor)
|
testUndoLike.AddActorObject(sallyActor)
|
||||||
testUndoLike.AddObject(testLikeNote)
|
testUndoLike.AddObject(testLikeNote)
|
||||||
testUndoLike.AddToObject(samActor)
|
testUndoLike.AddToObject(samActor)
|
||||||
|
|
||||||
|
testClientExpectedNote = &vocab.Note{}
|
||||||
|
testClientExpectedNote.SetId(*noteIRI)
|
||||||
|
testClientExpectedNote.AddNameString(noteName)
|
||||||
|
testClientExpectedNote.AddContentString("This is a simple note")
|
||||||
|
testClientExpectedNote.AddAttributedToObject(sallyActor)
|
||||||
|
testClientExpectedNote.AddToObject(samActor)
|
||||||
|
testClientExpectedCreateNote = &vocab.Create{}
|
||||||
|
testClientExpectedCreateNote.SetId(*testNewIRI)
|
||||||
|
testClientExpectedCreateNote.AddSummaryString("Sally created a note")
|
||||||
|
testClientExpectedCreateNote.AddActorObject(sallyActor)
|
||||||
|
testClientExpectedCreateNote.AddObject(testClientExpectedNote)
|
||||||
|
testClientExpectedCreateNote.AddToObject(samActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Must(l *time.Location, e error) *time.Location {
|
func Must(l *time.Location, e error) *time.Location {
|
||||||
|
@ -752,9 +767,9 @@ func TestSocialPubber_PostOutbox(t *testing.T) {
|
||||||
app.set = func(c context.Context, o PubObject) error {
|
app.set = func(c context.Context, o PubObject) error {
|
||||||
gotSet++
|
gotSet++
|
||||||
if gotSet == 1 {
|
if gotSet == 1 {
|
||||||
gotSetOutbox = o
|
|
||||||
} else if gotSet == 2 {
|
|
||||||
gotSetCreateObject = o
|
gotSetCreateObject = o
|
||||||
|
} else if gotSet == 2 {
|
||||||
|
gotSetOutbox = o
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1038,9 +1053,9 @@ func TestPubber_PostOutbox(t *testing.T) {
|
||||||
app.set = func(c context.Context, o PubObject) error {
|
app.set = func(c context.Context, o PubObject) error {
|
||||||
gotSet++
|
gotSet++
|
||||||
if gotSet == 1 {
|
if gotSet == 1 {
|
||||||
gotSetOutbox = o
|
|
||||||
} else if gotSet == 2 {
|
|
||||||
gotSetCreateObject = o
|
gotSetCreateObject = o
|
||||||
|
} else if gotSet == 2 {
|
||||||
|
gotSetOutbox = o
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2892,23 +2907,130 @@ func TestPostOutbox_RequiresTarget(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOutbox_Create_CopyToAttributedTo(t *testing.T) {
|
func TestPostOutbox_Create_CopyToAttributedTo(t *testing.T) {
|
||||||
// TODO: Implement
|
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
|
||||||
}
|
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
func TestPostOutbox_Create_CopyRecipientsToObject(t *testing.T) {
|
req := ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(testCreateNote))))
|
||||||
// TODO: Implement
|
var gotCallbackObject *streams.Create
|
||||||
|
socialCb.create = func(c context.Context, s *streams.Create) error {
|
||||||
|
gotCallbackObject = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
handled, err := p.PostOutbox(context.Background(), resp, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !handled {
|
||||||
|
t.Fatalf("expected handled, got !handled")
|
||||||
|
} else if e := PubObjectEquals(gotCallbackObject.Raw(), testClientExpectedCreateNote); e != nil {
|
||||||
|
t.Fatal(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOutbox_Create_SetCreatedObject(t *testing.T) {
|
func TestPostOutbox_Create_SetCreatedObject(t *testing.T) {
|
||||||
// TODO: Implement
|
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
|
||||||
|
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
req := ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(testCreateNote))))
|
||||||
|
socialCb.create = func(c context.Context, s *streams.Create) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
gotSet := 0
|
||||||
|
var gotSetOutbox PubObject
|
||||||
|
var gotSetCreate PubObject
|
||||||
|
app.set = func(c context.Context, o PubObject) error {
|
||||||
|
gotSet++
|
||||||
|
if gotSet == 1 {
|
||||||
|
gotSetCreate = o
|
||||||
|
} else {
|
||||||
|
gotSetOutbox = o
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
handled, err := p.PostOutbox(context.Background(), resp, req)
|
||||||
|
expectedOutbox := &vocab.OrderedCollection{}
|
||||||
|
expectedOutbox.AddType("OrderedCollection")
|
||||||
|
expectedOutbox.AddOrderedItemsObject(testClientExpectedCreateNote)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !handled {
|
||||||
|
t.Fatalf("expected handled, got !handled")
|
||||||
|
} else if gotSet != 2 {
|
||||||
|
t.Fatalf("expected %d, got %d", 2, gotSet)
|
||||||
|
} else if err := PubObjectEquals(gotSetCreate, testClientExpectedNote); err != nil {
|
||||||
|
t.Fatalf("unexpected callback object: %s", err)
|
||||||
|
} else if err := PubObjectEquals(gotSetOutbox, expectedOutbox); err != nil {
|
||||||
|
t.Fatalf("unexpected callback object: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOutbox_Create_CallsCallback(t *testing.T) {
|
func TestPostOutbox_Create_CallsCallback(t *testing.T) {
|
||||||
// TODO: Implement
|
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
|
||||||
|
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
req := ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(testCreateNote))))
|
||||||
|
gotCallback := 0
|
||||||
|
var gotCallbackObject *streams.Create
|
||||||
|
socialCb.create = func(c context.Context, s *streams.Create) error {
|
||||||
|
gotCallback++
|
||||||
|
gotCallbackObject = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
handled, err := p.PostOutbox(context.Background(), resp, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !handled {
|
||||||
|
t.Fatalf("expected handled, got !handled")
|
||||||
|
} else if gotCallback != 1 {
|
||||||
|
t.Fatalf("expected %d, got %d", 1, gotCallback)
|
||||||
|
} else if err := PubObjectEquals(gotCallbackObject.Raw(), testClientExpectedCreateNote); err != nil {
|
||||||
|
t.Fatalf("unexpected callback object: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOutbox_Create_IsDelivered(t *testing.T) {
|
func TestPostOutbox_Create_IsDelivered(t *testing.T) {
|
||||||
// TODO: Implement
|
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
|
||||||
|
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
req := ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(testCreateNote))))
|
||||||
|
socialCb.create = func(c context.Context, s *streams.Create) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
gotHttpDo := 0
|
||||||
|
var httpDeliveryRequest *http.Request
|
||||||
|
httpClient.do = func(req *http.Request) (*http.Response, error) {
|
||||||
|
gotHttpDo++
|
||||||
|
if gotHttpDo == 1 {
|
||||||
|
actorResp := &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewBuffer(samActorJSON)),
|
||||||
|
}
|
||||||
|
return actorResp, nil
|
||||||
|
} else if gotHttpDo == 2 {
|
||||||
|
actorResp := &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewBuffer(sallyActorJSON)),
|
||||||
|
}
|
||||||
|
return actorResp, nil
|
||||||
|
} else if gotHttpDo == 3 {
|
||||||
|
httpDeliveryRequest = req
|
||||||
|
okResp := &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewBuffer([]byte{})),
|
||||||
|
}
|
||||||
|
return okResp, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
handled, err := p.PostOutbox(context.Background(), resp, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !handled {
|
||||||
|
t.Fatalf("expected handled, got !handled")
|
||||||
|
} else if httpDeliveryRequest.Method != "POST" {
|
||||||
|
t.Fatalf("expected %s, got %s", "POST", httpDeliveryRequest.Method)
|
||||||
|
} else if s := httpDeliveryRequest.URL.String(); s != samIRIInboxString {
|
||||||
|
t.Fatalf("expected %s, got %s", samIRIInboxString, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOutbox_Update_OverwriteUpdatedFields(t *testing.T) {
|
func TestPostOutbox_Update_OverwriteUpdatedFields(t *testing.T) {
|
||||||
|
|
301
pub/internal.go
301
pub/internal.go
|
@ -173,6 +173,307 @@ func (f *federator) wrapInCreate(o vocab.ObjectType, actor url.URL) *vocab.Creat
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures the activity and object have the same 'to', 'bto', 'cc', 'bcc', and
|
||||||
|
// 'audience' properties. Copy the Activity's recipients to objects, and the
|
||||||
|
// objects to the activity, but does NOT copy objects' recipients to each other.
|
||||||
|
//
|
||||||
|
// If there is any disagreement between the activity and an object, we default
|
||||||
|
// to a no-op.
|
||||||
|
func (f *federator) sameRecipients(a vocab.ActivityType) error {
|
||||||
|
// First, map recipients for each object and the activity.
|
||||||
|
to := make([]map[string]interface{}, a.ObjectLen())
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
to[i] = make(map[string]interface{})
|
||||||
|
if !a.IsObject(i) {
|
||||||
|
return fmt.Errorf("sameRecipients does not support 'to' object IRIs on Activities")
|
||||||
|
}
|
||||||
|
o := a.GetObject(i)
|
||||||
|
for j := 0; j < o.ToLen(); j++ {
|
||||||
|
if o.IsToObject(j) {
|
||||||
|
id := o.GetToObject(j).GetId()
|
||||||
|
to[i][(&id).String()] = o.GetToObject(j)
|
||||||
|
} else if o.IsToLink(j) {
|
||||||
|
id := o.GetToLink(j).GetHref()
|
||||||
|
to[i][(&id).String()] = o.GetToLink(j)
|
||||||
|
} else if o.IsToIRI(j) {
|
||||||
|
id := o.GetToIRI(j)
|
||||||
|
to[i][(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toActivity := make(map[string]interface{})
|
||||||
|
for i := 0; i < a.ToLen(); i++ {
|
||||||
|
if a.IsToObject(i) {
|
||||||
|
id := a.GetToObject(i).GetId()
|
||||||
|
toActivity[(&id).String()] = a.GetToObject(i)
|
||||||
|
} else if a.IsToLink(i) {
|
||||||
|
id := a.GetToLink(i).GetHref()
|
||||||
|
toActivity[(&id).String()] = a.GetToLink(i)
|
||||||
|
} else if a.IsToIRI(i) {
|
||||||
|
id := a.GetToIRI(i)
|
||||||
|
toActivity[(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bto := make([]map[string]interface{}, a.ObjectLen())
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
bto[i] = make(map[string]interface{})
|
||||||
|
if !a.IsObject(i) {
|
||||||
|
return fmt.Errorf("sameRecipients does not support 'bto' object IRIs on Activities")
|
||||||
|
}
|
||||||
|
o := a.GetObject(i)
|
||||||
|
for j := 0; j < o.BtoLen(); j++ {
|
||||||
|
if o.IsBtoObject(j) {
|
||||||
|
id := o.GetBtoObject(j).GetId()
|
||||||
|
bto[i][(&id).String()] = o.GetBtoObject(j)
|
||||||
|
} else if o.IsBtoLink(j) {
|
||||||
|
id := o.GetBtoLink(j).GetHref()
|
||||||
|
bto[i][(&id).String()] = o.GetBtoLink(j)
|
||||||
|
} else if o.IsBtoIRI(j) {
|
||||||
|
id := o.GetBtoIRI(j)
|
||||||
|
bto[i][(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
btoActivity := make(map[string]interface{})
|
||||||
|
for i := 0; i < a.BtoLen(); i++ {
|
||||||
|
if a.IsBtoObject(i) {
|
||||||
|
id := a.GetBtoObject(i).GetId()
|
||||||
|
btoActivity[(&id).String()] = a.GetBtoObject(i)
|
||||||
|
} else if a.IsBtoLink(i) {
|
||||||
|
id := a.GetBtoLink(i).GetHref()
|
||||||
|
btoActivity[(&id).String()] = a.GetBtoLink(i)
|
||||||
|
} else if a.IsBtoIRI(i) {
|
||||||
|
id := a.GetBtoIRI(i)
|
||||||
|
btoActivity[(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cc := make([]map[string]interface{}, a.ObjectLen())
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
cc[i] = make(map[string]interface{})
|
||||||
|
if !a.IsObject(i) {
|
||||||
|
return fmt.Errorf("sameRecipients does not support 'cc' object IRIs on Activities")
|
||||||
|
}
|
||||||
|
o := a.GetObject(i)
|
||||||
|
for j := 0; j < o.CcLen(); j++ {
|
||||||
|
if o.IsCcObject(j) {
|
||||||
|
id := o.GetCcObject(j).GetId()
|
||||||
|
cc[i][(&id).String()] = o.GetCcObject(j)
|
||||||
|
} else if o.IsCcLink(j) {
|
||||||
|
id := o.GetCcLink(j).GetHref()
|
||||||
|
cc[i][(&id).String()] = o.GetCcLink(j)
|
||||||
|
} else if o.IsCcIRI(j) {
|
||||||
|
id := o.GetCcIRI(j)
|
||||||
|
cc[i][(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ccActivity := make(map[string]interface{})
|
||||||
|
for i := 0; i < a.CcLen(); i++ {
|
||||||
|
if a.IsCcObject(i) {
|
||||||
|
id := a.GetCcObject(i).GetId()
|
||||||
|
ccActivity[(&id).String()] = a.GetCcObject(i)
|
||||||
|
} else if a.IsCcLink(i) {
|
||||||
|
id := a.GetCcLink(i).GetHref()
|
||||||
|
ccActivity[(&id).String()] = a.GetCcLink(i)
|
||||||
|
} else if a.IsCcIRI(i) {
|
||||||
|
id := a.GetCcIRI(i)
|
||||||
|
ccActivity[(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bcc := make([]map[string]interface{}, a.ObjectLen())
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
bcc[i] = make(map[string]interface{})
|
||||||
|
if !a.IsObject(i) {
|
||||||
|
return fmt.Errorf("sameRecipients does not support 'bcc' object IRIs on Activities")
|
||||||
|
}
|
||||||
|
o := a.GetObject(i)
|
||||||
|
for j := 0; j < o.BccLen(); j++ {
|
||||||
|
if o.IsBccObject(j) {
|
||||||
|
id := o.GetBccObject(j).GetId()
|
||||||
|
bcc[i][(&id).String()] = o.GetBccObject(j)
|
||||||
|
} else if o.IsBccLink(j) {
|
||||||
|
id := o.GetBccLink(j).GetHref()
|
||||||
|
bcc[i][(&id).String()] = o.GetBccLink(j)
|
||||||
|
} else if o.IsBccIRI(j) {
|
||||||
|
id := o.GetBccIRI(j)
|
||||||
|
bcc[i][(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bccActivity := make(map[string]interface{})
|
||||||
|
for i := 0; i < a.BccLen(); i++ {
|
||||||
|
if a.IsBccObject(i) {
|
||||||
|
id := a.GetBccObject(i).GetId()
|
||||||
|
bccActivity[(&id).String()] = a.GetBccObject(i)
|
||||||
|
} else if a.IsBccLink(i) {
|
||||||
|
id := a.GetBccLink(i).GetHref()
|
||||||
|
bccActivity[(&id).String()] = a.GetBccLink(i)
|
||||||
|
} else if a.IsBccIRI(i) {
|
||||||
|
id := a.GetBccIRI(i)
|
||||||
|
bccActivity[(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audience := make([]map[string]interface{}, a.ObjectLen())
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
audience[i] = make(map[string]interface{})
|
||||||
|
if !a.IsObject(i) {
|
||||||
|
return fmt.Errorf("sameRecipients does not support 'audience' object IRIs on Activities")
|
||||||
|
}
|
||||||
|
o := a.GetObject(i)
|
||||||
|
for j := 0; j < o.AudienceLen(); j++ {
|
||||||
|
if o.IsAudienceObject(j) {
|
||||||
|
id := o.GetAudienceObject(j).GetId()
|
||||||
|
audience[i][(&id).String()] = o.GetAudienceObject(j)
|
||||||
|
} else if o.IsAudienceLink(j) {
|
||||||
|
id := o.GetAudienceLink(j).GetHref()
|
||||||
|
audience[i][(&id).String()] = o.GetAudienceLink(j)
|
||||||
|
} else if o.IsAudienceIRI(j) {
|
||||||
|
id := o.GetAudienceIRI(j)
|
||||||
|
audience[i][(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audienceActivity := make(map[string]interface{})
|
||||||
|
for i := 0; i < a.AudienceLen(); i++ {
|
||||||
|
if a.IsAudienceObject(i) {
|
||||||
|
id := a.GetAudienceObject(i).GetId()
|
||||||
|
audienceActivity[(&id).String()] = a.GetAudienceObject(i)
|
||||||
|
} else if a.IsAudienceLink(i) {
|
||||||
|
id := a.GetAudienceLink(i).GetHref()
|
||||||
|
audienceActivity[(&id).String()] = a.GetAudienceLink(i)
|
||||||
|
} else if a.IsAudienceIRI(i) {
|
||||||
|
id := a.GetAudienceIRI(i)
|
||||||
|
audienceActivity[(&id).String()] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Next, add activity recipients to all objects if not already present
|
||||||
|
for k, v := range toActivity {
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
if _, ok := to[i][k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.GetObject(i).AddToObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.GetObject(i).AddToLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.GetObject(i).AddToIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range btoActivity {
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
if _, ok := bto[i][k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.GetObject(i).AddBtoObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.GetObject(i).AddBtoLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.GetObject(i).AddBtoIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range ccActivity {
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
if _, ok := cc[i][k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.GetObject(i).AddCcObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.GetObject(i).AddCcLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.GetObject(i).AddCcIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range bccActivity {
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
if _, ok := bcc[i][k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.GetObject(i).AddBccObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.GetObject(i).AddBccLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.GetObject(i).AddBccIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range audienceActivity {
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
if _, ok := audience[i][k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.GetObject(i).AddAudienceObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.GetObject(i).AddAudienceLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.GetObject(i).AddAudienceIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finally, add all the objects' recipients to the activity if not
|
||||||
|
// already present.
|
||||||
|
for i := 0; i < a.ObjectLen(); i++ {
|
||||||
|
for k, v := range to[i] {
|
||||||
|
if _, ok := toActivity[k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.AddToObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.AddToLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.AddToIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range bto[i] {
|
||||||
|
if _, ok := btoActivity[k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.AddBtoObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.AddBtoLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.AddBtoIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range cc[i] {
|
||||||
|
if _, ok := ccActivity[k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.AddCcObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.AddCcLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.AddCcIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range bcc[i] {
|
||||||
|
if _, ok := bccActivity[k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.AddBccObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.AddBccLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.AddBccIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range audience[i] {
|
||||||
|
if _, ok := audienceActivity[k]; !ok {
|
||||||
|
if vObj, ok := v.(vocab.ObjectType); ok {
|
||||||
|
a.AddAudienceObject(vObj)
|
||||||
|
} else if vLink, ok := v.(vocab.LinkType); ok {
|
||||||
|
a.AddAudienceLink(vLink)
|
||||||
|
} else if vIRI, ok := v.(url.URL); ok {
|
||||||
|
a.AddAudienceIRI(vIRI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: (Section 7) HTTP caching mechanisms [RFC7234] SHOULD be respected when appropriate, both when receiving responses from other servers as well as sending responses to other servers.
|
// TODO: (Section 7) HTTP caching mechanisms [RFC7234] SHOULD be respected when appropriate, both when receiving responses from other servers as well as sending responses to other servers.
|
||||||
|
|
||||||
// deliver will complete the peer-to-peer sending of a federated message to
|
// deliver will complete the peer-to-peer sending of a federated message to
|
||||||
|
|
読み込み中…
新しいイシューから参照