added shared inbox serialization

This commit is contained in:
Nicolas Constant 2020-08-10 20:04:12 -04:00
parent 9f574ea4b2
commit d5276c120e
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688
9 changed files with 97 additions and 52 deletions

View file

@ -73,27 +73,8 @@ namespace BirdsiteLive.Domain
to = note.to, to = note.to,
cc = note.cc, cc = note.cc,
apObject = note apObject = note
//apObject = new Note()
//{
// id = noteUri,
// summary = null,
// inReplyTo = null,
// published = nowString,
// url = noteUrl,
// attributedTo = actor,
// to = new[] { to },
// //cc = new [] { apPublic },
// sensitive = false,
// content = "<p>Woooot</p>",
// attachment = new string[0],
// tag = new string[0]
//}
}; };
//TODO Remove this
if (targetInbox.Contains(targetHost))
targetInbox = targetInbox.Split(new []{ targetHost }, StringSplitOptions.RemoveEmptyEntries).Last();
return await PostDataAsync(noteActivity, targetHost, actor, targetInbox); return await PostDataAsync(noteActivity, targetHost, actor, targetInbox);
} }

View file

@ -5,7 +5,7 @@ namespace BirdsiteLive.Domain.BusinessUseCases
{ {
public interface IProcessFollowUser public interface IProcessFollowUser
{ {
Task ExecuteAsync(string followerUsername, string followerDomain, string followerInbox, string twitterUser); Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox);
} }
public class ProcessFollowUser : IProcessFollowUser public class ProcessFollowUser : IProcessFollowUser
@ -21,13 +21,13 @@ namespace BirdsiteLive.Domain.BusinessUseCases
} }
#endregion #endregion
public async Task ExecuteAsync(string followerUsername, string followerDomain, string followerInbox, string twitterUsername) public async Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox)
{ {
// Get Follower and Twitter Users // Get Follower and Twitter Users
var follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain); var follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
if (follower == null) if (follower == null)
{ {
await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox); await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox, sharedInbox);
follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain); follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
} }

View file

@ -88,8 +88,15 @@ namespace BirdsiteLive.Domain
var followerUserName = sigValidation.User.name.ToLowerInvariant(); var followerUserName = sigValidation.User.name.ToLowerInvariant();
var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First(); var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First();
var followerInbox = sigValidation.User.inbox; var followerInbox = sigValidation.User.inbox;
var followerSharedInbox = sigValidation.User?.endpoints?.sharedInbox;
var twitterUser = activity.apObject.Split('/').Last().Replace("@", string.Empty); var twitterUser = activity.apObject.Split('/').Last().Replace("@", string.Empty);
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, followerInbox, twitterUser);
// Make sure to only keep routes
followerInbox = OnlyKeepRoute(followerInbox, followerHost);
followerSharedInbox = OnlyKeepRoute(followerSharedInbox, followerHost);
// Execute
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox);
// Send Accept Activity // Send Accept Activity
var acceptFollow = new ActivityAcceptFollow() var acceptFollow = new ActivityAcceptFollow()
@ -110,6 +117,17 @@ namespace BirdsiteLive.Domain
return result == HttpStatusCode.Accepted; return result == HttpStatusCode.Accepted;
} }
private string OnlyKeepRoute(string inbox, string host)
{
if (string.IsNullOrWhiteSpace(inbox))
return null;
if (inbox.Contains(host))
inbox = inbox.Split(new[] { host }, StringSplitOptions.RemoveEmptyEntries).Last();
return inbox;
}
public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString,
Dictionary<string, string> requestHeaders, ActivityUndoFollow activity) Dictionary<string, string> requestHeaders, ActivityUndoFollow activity)
{ {

View file

@ -51,11 +51,13 @@ namespace BirdsiteLive.Pipeline.Processors
return userWithTweetsToSync; return userWithTweetsToSync;
} }
private async Task ProcessFollowerAsync(IEnumerable<ExtractedTweet> tweets, Follower follower, int userId, private async Task ProcessFollowerAsync(IEnumerable<ExtractedTweet> tweets, Follower follower, int userId, SyncTwitterUser user)
SyncTwitterUser user)
{ {
var fromStatusId = follower.FollowingsSyncStatus[userId]; var fromStatusId = follower.FollowingsSyncStatus[userId];
var tweetsToSend = tweets.Where(x => x.Id > fromStatusId).OrderBy(x => x.Id).ToList(); var tweetsToSend = tweets.Where(x => x.Id > fromStatusId).OrderBy(x => x.Id).ToList();
var inbox = string.IsNullOrWhiteSpace(follower.SharedInboxRoute)
? follower.InboxRoute
: follower.SharedInboxRoute;
var syncStatus = fromStatusId; var syncStatus = fromStatusId;
try try
@ -63,8 +65,7 @@ namespace BirdsiteLive.Pipeline.Processors
foreach (var tweet in tweetsToSend) foreach (var tweet in tweetsToSend)
{ {
var note = _statusService.GetStatus(user.Acct, tweet); var note = _statusService.GetStatus(user.Acct, tweet);
var result = await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host, var result = await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host, inbox);
follower.InboxUrl);
if (result == HttpStatusCode.Accepted) if (result == HttpStatusCode.Accepted)
syncStatus = tweet.Id; syncStatus = tweet.Id;

View file

@ -108,7 +108,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
acct VARCHAR(50) NOT NULL, acct VARCHAR(50) NOT NULL,
host VARCHAR(253) NOT NULL, host VARCHAR(253) NOT NULL,
inboxUrl VARCHAR(2048) NOT NULL, inboxRoute VARCHAR(2048) NOT NULL,
sharedInboxRoute VARCHAR(2048),
UNIQUE (acct, host) UNIQUE (acct, host)
);"; );";
await _tools.ExecuteRequestAsync(createFollowers); await _tools.ExecuteRequestAsync(createFollowers);

View file

@ -20,7 +20,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
} }
#endregion #endregion
public async Task CreateFollowerAsync(string acct, string host, string inboxUrl, int[] followings = null, Dictionary<int, long> followingSyncStatus = null) public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, int[] followings = null, Dictionary<int, long> followingSyncStatus = null)
{ {
if(followings == null) followings = new int[0]; if(followings == null) followings = new int[0];
if(followingSyncStatus == null) followingSyncStatus = new Dictionary<int, long>(); if(followingSyncStatus == null) followingSyncStatus = new Dictionary<int, long>();
@ -35,8 +35,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
dbConnection.Open(); dbConnection.Open();
await dbConnection.ExecuteAsync( await dbConnection.ExecuteAsync(
$"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxUrl,followings,followingsSyncStatus) VALUES(@acct,@host,@inboxUrl,@followings,CAST(@followingsSyncStatus as json))", $"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxRoute,sharedInboxRoute,followings,followingsSyncStatus) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,CAST(@followingsSyncStatus as json))",
new { acct, host, inboxUrl, followings, followingsSyncStatus = serializedDic }); new { acct, host, inboxRoute, sharedInboxRoute, followings, followingsSyncStatus = serializedDic });
} }
} }
@ -128,7 +128,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
Id = follower.Id, Id = follower.Id,
Acct = follower.Acct, Acct = follower.Acct,
Host = follower.Host, Host = follower.Host,
InboxUrl = follower.InboxUrl, InboxRoute = follower.InboxRoute,
SharedInboxRoute = follower.SharedInboxRoute,
Followings = follower.Followings.ToList(), Followings = follower.Followings.ToList(),
FollowingsSyncStatus = JsonConvert.DeserializeObject<Dictionary<int,long>>(follower.FollowingsSyncStatus) FollowingsSyncStatus = JsonConvert.DeserializeObject<Dictionary<int,long>>(follower.FollowingsSyncStatus)
}; };
@ -143,6 +144,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
public string Acct { get; set; } public string Acct { get; set; }
public string Host { get; set; } public string Host { get; set; }
public string InboxUrl { get; set; } public string InboxRoute { get; set; }
public string SharedInboxRoute { get; set; }
} }
} }

View file

@ -7,7 +7,7 @@ namespace BirdsiteLive.DAL.Contracts
public interface IFollowersDal public interface IFollowersDal
{ {
Task<Follower> GetFollowerAsync(string acct, string host); Task<Follower> GetFollowerAsync(string acct, string host);
Task CreateFollowerAsync(string acct, string host, string inboxUrl, int[] followings = null, Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, int[] followings = null,
Dictionary<int, long> followingSyncStatus = null); Dictionary<int, long> followingSyncStatus = null);
Task<Follower[]> GetFollowersAsync(int followedUserId); Task<Follower[]> GetFollowersAsync(int followedUserId);
Task UpdateFollowerAsync(Follower follower); Task UpdateFollowerAsync(Follower follower);

View file

@ -11,6 +11,7 @@ namespace BirdsiteLive.DAL.Models
public string Acct { get; set; } public string Acct { get; set; }
public string Host { get; set; } public string Host { get; set; }
public string InboxUrl { get; set; } public string InboxRoute { get; set; }
public string SharedInboxRoute { get; set; }
} }
} }

View file

@ -45,17 +45,51 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{19, 166L}, {19, 166L},
{23, 167L} {23, 167L}
}; };
var inboxUrl = "https://domain.ext/myhandle/inbox"; var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var dal = new FollowersPostgresDal(_settings); var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host); var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result); Assert.IsNotNull(result);
Assert.AreEqual(acct, result.Acct); Assert.AreEqual(acct, result.Acct);
Assert.AreEqual(host, result.Host); Assert.AreEqual(host, result.Host);
Assert.AreEqual(inboxUrl, result.InboxUrl); Assert.AreEqual(inboxRoute, result.InboxRoute);
Assert.AreEqual(sharedInboxRoute, result.SharedInboxRoute);
Assert.AreEqual(following.Length, result.Followings.Count);
Assert.AreEqual(following[0], result.Followings[0]);
Assert.AreEqual(followingSync.Count, result.FollowingsSyncStatus.Count);
Assert.AreEqual(followingSync.First().Key, result.FollowingsSyncStatus.First().Key);
Assert.AreEqual(followingSync.First().Value, result.FollowingsSyncStatus.First().Value);
}
[TestMethod]
public async Task CreateAndGetFollower_NoSharedInbox()
{
var acct = "myhandle";
var host = "domain.ext";
var following = new[] { 12, 19, 23 };
var followingSync = new Dictionary<int, long>()
{
{12, 165L},
{19, 166L},
{23, 167L}
};
var inboxRoute = "/myhandle/inbox";
string sharedInboxRoute = null;
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result);
Assert.AreEqual(acct, result.Acct);
Assert.AreEqual(host, result.Host);
Assert.AreEqual(inboxRoute, result.InboxRoute);
Assert.AreEqual(sharedInboxRoute, result.SharedInboxRoute);
Assert.AreEqual(following.Length, result.Followings.Count); Assert.AreEqual(following.Length, result.Followings.Count);
Assert.AreEqual(following[0], result.Followings[0]); Assert.AreEqual(following[0], result.Followings[0]);
Assert.AreEqual(followingSync.Count, result.FollowingsSyncStatus.Count); Assert.AreEqual(followingSync.Count, result.FollowingsSyncStatus.Count);
@ -73,22 +107,25 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
var host = "domain.ext"; var host = "domain.ext";
var following = new[] { 1,2,3 }; var following = new[] { 1,2,3 };
var followingSync = new Dictionary<int, long>(); var followingSync = new Dictionary<int, long>();
var inboxUrl = "https://domain.ext/myhandle1/inbox"; var inboxRoute = "/myhandle1/inbox";
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); var sharedInboxRoute = "/inbox";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
//User 2 //User 2
acct = "myhandle2"; acct = "myhandle2";
host = "domain.ext"; host = "domain.ext";
following = new[] { 2, 4, 5 }; following = new[] { 2, 4, 5 };
inboxUrl = "https://domain.ext/myhandle2/inbox"; inboxRoute = "/myhandle2/inbox";
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); sharedInboxRoute = "/inbox2";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
//User 2 //User 2
acct = "myhandle3"; acct = "myhandle3";
host = "domain.ext"; host = "domain.ext";
following = new[] { 1 }; following = new[] { 1 };
inboxUrl = "https://domain.ext/myhandle3/inbox"; inboxRoute = "/myhandle3/inbox";
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); sharedInboxRoute = "/inbox3";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowersAsync(2); var result = await dal.GetFollowersAsync(2);
Assert.AreEqual(2, result.Length); Assert.AreEqual(2, result.Length);
@ -112,10 +149,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{19, 166L}, {19, 166L},
{23, 167L} {23, 167L}
}; };
var inboxUrl = "https://domain.ext/myhandle/inbox"; var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var dal = new FollowersPostgresDal(_settings); var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host); var result = await dal.GetFollowerAsync(acct, host);
var updatedFollowing = new List<int> { 12, 19, 23, 24 }; var updatedFollowing = new List<int> { 12, 19, 23, 24 };
@ -151,10 +189,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{19, 166L}, {19, 166L},
{23, 167L} {23, 167L}
}; };
var inboxUrl = "https://domain.ext/myhandle/inbox"; var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var dal = new FollowersPostgresDal(_settings); var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host); var result = await dal.GetFollowerAsync(acct, host);
var updatedFollowing = new[] { 12, 19 }; var updatedFollowing = new[] { 12, 19 };
@ -188,10 +227,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{19, 166L}, {19, 166L},
{23, 167L} {23, 167L}
}; };
var inboxUrl = "https://domain.ext/myhandle/inbox"; var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var dal = new FollowersPostgresDal(_settings); var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host); var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result); Assert.IsNotNull(result);
@ -213,10 +253,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{19, 166L}, {19, 166L},
{23, 167L} {23, 167L}
}; };
var inboxUrl = "https://domain.ext/myhandle/inbox"; var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var dal = new FollowersPostgresDal(_settings); var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync); await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host); var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result); Assert.IsNotNull(result);