From b23a710533803e20daf3b5e450b3281562a38748 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Sun, 5 Jul 2020 22:57:05 -0400 Subject: [PATCH 1/8] implementing TwitterDal --- .../CachedTweetsPostgresDal.cs | 34 +++++++ .../TwitterUserPostgresDal.cs | 74 +++++++++++++- .../BirdsiteLive.DAL/BirdsiteLive.DAL.csproj | 4 + .../Contracts/ICachedTweetsDal.cs | 12 +++ .../Contracts/ITwitterUserDal.cs | 11 ++- .../Base/PostgresTestingBase.cs | 27 +++++ .../DbInitializerPostgresDalTests.cs | 24 +---- .../TwitterUserPostgresDalTests.cs | 99 +++++++++++++++++++ 8 files changed, 258 insertions(+), 27 deletions(-) create mode 100644 src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs create mode 100644 src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs create mode 100644 src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/Base/PostgresTestingBase.cs create mode 100644 src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs new file mode 100644 index 0000000..3251c41 --- /dev/null +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base; +using BirdsiteLive.DAL.Postgres.Settings; +using BirdsiteLive.DAL.Postgres.Tools; +using Tweetinvi.Models; + +namespace BirdsiteLive.DAL.Postgres.DataAccessLayers +{ + public class CachedTweetsPostgresDal : PostgresBase, ICachedTweetsDal + { + #region Ctor + public CachedTweetsPostgresDal(PostgresSettings settings, PostgresTools tools) : base(settings) + { + + } + #endregion + + public Task AddTweetAsync(long tweetId, int userId, ITweet tweet) + { + throw new System.NotImplementedException(); + } + + public Task GetTweetAsync(long tweetId) + { + throw new System.NotImplementedException(); + } + + public Task DeleteTweetAsync(long tweetId) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs index 2058bf3..94e1087 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs @@ -1,9 +1,77 @@ -using BirdsiteLive.DAL.Contracts; +using System; +using System.Linq; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Models; +using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base; +using BirdsiteLive.DAL.Postgres.Settings; +using BirdsiteLive.DAL.Postgres.Tools; +using Dapper; +using Npgsql; namespace BirdsiteLive.DAL.Postgres.DataAccessLayers { - public class TwitterUserPostgresDal : ITwitterUserDal + public class TwitterUserPostgresDal : PostgresBase, ITwitterUserDal { - + #region Ctor + public TwitterUserPostgresDal(PostgresSettings settings) : base(settings) + { + + } + #endregion + + public async Task CreateTwitterUserAsync(string acct, long lastTweetPostedId) + { + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.ExecuteAsync( + $"INSERT INTO {_settings.TwitterUserTableName} (acct,lastTweetPostedId,lastTweetSynchronizedForAllFollowersId) VALUES(@acct,@lastTweetPostedId,@lastTweetSynchronizedForAllFollowersId)", + new { acct = acct, lastTweetPostedId = lastTweetPostedId, lastTweetSynchronizedForAllFollowersId = lastTweetPostedId }); + } + } + + public async Task GetTwitterUserAsync(string acct) + { + var query = $"SELECT * FROM {_settings.TwitterUserTableName} WHERE acct = @acct"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + var result = (await dbConnection.QueryAsync(query, new { acct = acct })).FirstOrDefault(); + return result; + } + } + + public Task GetAllTwitterUsersAsync() + { + throw new System.NotImplementedException(); + } + + public async Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId) + { + if(id == default) throw new ArgumentException("id"); + if(lastTweetPostedId == default) throw new ArgumentException("lastTweetPostedId"); + if(lastTweetSynchronizedForAllFollowersId == default) throw new ArgumentException("lastTweetSynchronizedForAllFollowersId"); + + var query = $"UPDATE {_settings.TwitterUserTableName} SET lastTweetPostedId = {lastTweetPostedId}, lastTweetSynchronizedForAllFollowersId = {lastTweetSynchronizedForAllFollowersId} WHERE id = {id}"; + + using (var dbConnection = Connection) + using (var cmd = new NpgsqlCommand(query, dbConnection)) + { + dbConnection.Open(); + await cmd.ExecuteNonQueryAsync(); + } + } + + public Task DeleteTwitterUserAsync(string acct) + { + + + + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/BirdsiteLive.DAL.csproj b/src/DataAccessLayers/BirdsiteLive.DAL/BirdsiteLive.DAL.csproj index 9f5c4f4..84e0bf0 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/BirdsiteLive.DAL.csproj +++ b/src/DataAccessLayers/BirdsiteLive.DAL/BirdsiteLive.DAL.csproj @@ -4,4 +4,8 @@ netstandard2.0 + + + + diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs new file mode 100644 index 0000000..d1214a5 --- /dev/null +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Tweetinvi.Models; + +namespace BirdsiteLive.DAL.Contracts +{ + public interface ICachedTweetsDal + { + Task AddTweetAsync(long tweetId, int userId, ITweet tweet); + Task GetTweetAsync(long tweetId); + Task DeleteTweetAsync(long tweetId); + } +} \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs index 195b49b..e0050fd 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs @@ -1,7 +1,14 @@ -namespace BirdsiteLive.DAL.Contracts +using System.Threading.Tasks; +using BirdsiteLive.DAL.Models; + +namespace BirdsiteLive.DAL.Contracts { public interface ITwitterUserDal { - + Task CreateTwitterUserAsync(string acct, long lastTweetPostedId); + Task GetTwitterUserAsync(string acct); + Task GetAllTwitterUsersAsync(); + Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId); + Task DeleteTwitterUserAsync(string acct); } } \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/Base/PostgresTestingBase.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/Base/PostgresTestingBase.cs new file mode 100644 index 0000000..72bf352 --- /dev/null +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/Base/PostgresTestingBase.cs @@ -0,0 +1,27 @@ +using BirdsiteLive.DAL.Postgres.Settings; +using BirdsiteLive.DAL.Postgres.Tools; +using BirdsiteLive.DAL.Tools; + +namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base +{ + public class PostgresTestingBase + { + protected readonly PostgresSettings _settings; + protected readonly PostgresTools _tools; + + #region Ctor + public PostgresTestingBase() + { + _settings = new PostgresSettings + { + ConnString = "Host=127.0.0.1;Username=postgres;Password=mysecretpassword;Database=mytestdb", + DbVersionTableName = "DbVersionTableName" + RandomGenerator.GetString(4), + CachedTweetsTableName = "CachedTweetsTableName" + RandomGenerator.GetString(4), + FollowersTableName = "FollowersTableName" + RandomGenerator.GetString(4), + TwitterUserTableName = "TwitterUserTableName" + RandomGenerator.GetString(4), + }; + _tools = new PostgresTools(_settings); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/DbInitializerPostgresDalTests.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/DbInitializerPostgresDalTests.cs index 5ca5968..7fc5383 100644 --- a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/DbInitializerPostgresDalTests.cs +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/DbInitializerPostgresDalTests.cs @@ -1,34 +1,14 @@ using System; using System.Threading.Tasks; using BirdsiteLive.DAL.Postgres.DataAccessLayers; -using BirdsiteLive.DAL.Postgres.Settings; -using BirdsiteLive.DAL.Postgres.Tools; -using BirdsiteLive.DAL.Tools; +using BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers { [TestClass] - public class DbInitializerPostgresDalTests + public class DbInitializerPostgresDalTests : PostgresTestingBase { - private readonly PostgresSettings _settings; - private readonly PostgresTools _tools; - - #region Ctor - public DbInitializerPostgresDalTests() - { - _settings = new PostgresSettings - { - ConnString = "Host=127.0.0.1;Username=postgres;Password=mysecretpassword;Database=mytestdb", - DbVersionTableName = "DbVersionTableName" + RandomGenerator.GetString(4), - CachedTweetsTableName = "CachedTweetsTableName" + RandomGenerator.GetString(4), - FollowersTableName = "FollowersTableName" + RandomGenerator.GetString(4), - TwitterUserTableName = "TwitterUserTableName" + RandomGenerator.GetString(4), - }; - _tools = new PostgresTools(_settings); - } - #endregion - [TestCleanup] public async Task CleanUp() { diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs new file mode 100644 index 0000000..266a023 --- /dev/null +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading.Tasks; +using System.Xml; +using BirdsiteLive.DAL.Postgres.DataAccessLayers; +using BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers +{ + [TestClass] + public class TwitterUserPostgresDalTests : PostgresTestingBase + { + [TestInitialize] + public async Task TestInit() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + await dal.InitDbAsync(); + } + + [TestCleanup] + public async Task CleanUp() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + try + { + await dal.DeleteAllAsync(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + [TestMethod] + public async Task GetTwitterUserAsync_NoUser() + { + var dal = new TwitterUserPostgresDal(_settings); + var result = await dal.GetTwitterUserAsync("dontexist"); + Assert.IsNull(result); + } + + [TestMethod] + public async Task CreateAndGetUser() + { + var acct = "myid"; + var lastTweetId = 1548L; + + var dal = new TwitterUserPostgresDal(_settings); + + await dal.CreateTwitterUserAsync(acct, lastTweetId); + var result = await dal.GetTwitterUserAsync(acct); + + Assert.AreEqual(acct, result.Acct); + Assert.AreEqual(lastTweetId, result.LastTweetPostedId); + Assert.AreEqual(lastTweetId, result.LastTweetSynchronizedForAllFollowersId); + Assert.IsTrue(result.Id > 0); + } + + [TestMethod] + public async Task CreateUpdateAndGetUser() + { + var acct = "myid"; + var lastTweetId = 1548L; + + var dal = new TwitterUserPostgresDal(_settings); + + await dal.CreateTwitterUserAsync(acct, lastTweetId); + var result = await dal.GetTwitterUserAsync(acct); + + + var updatedLastTweetId = 1600L; + var updatedLastSyncId = 1550L; + await dal.UpdateTwitterUserAsync(result.Id, updatedLastTweetId, updatedLastSyncId); + + result = await dal.GetTwitterUserAsync(acct); + + Assert.AreEqual(acct, result.Acct); + Assert.AreEqual(updatedLastTweetId, result.LastTweetPostedId); + Assert.AreEqual(updatedLastSyncId, result.LastTweetSynchronizedForAllFollowersId); + } + + [TestMethod] + public async Task CreateAndDeleteUser() + { + var acct = "myid"; + var lastTweetId = 1548L; + + var dal = new TwitterUserPostgresDal(_settings); + + await dal.CreateTwitterUserAsync(acct, lastTweetId); + var result = await dal.GetTwitterUserAsync(acct); + Assert.IsNotNull(result); + + await dal.DeleteTwitterUserAsync(acct); + result = await dal.GetTwitterUserAsync(acct); + Assert.IsNull(result); + } + } +} \ No newline at end of file From b4b17564458016e52f7a2309a4277a13d20fcea8 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Sun, 5 Jul 2020 23:09:48 -0400 Subject: [PATCH 2/8] Ended TwitterUserPostgresDal + Tests --- .../TwitterUserPostgresDal.cs | 30 ++++++++++++++----- .../TwitterUserPostgresDalTests.cs | 20 +++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs index 94e1087..e02c6be 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs @@ -45,9 +45,17 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers } } - public Task GetAllTwitterUsersAsync() + public async Task GetAllTwitterUsersAsync() { - throw new System.NotImplementedException(); + var query = $"SELECT * FROM {_settings.TwitterUserTableName}"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + var result = await dbConnection.QueryAsync(query); + return result.ToArray(); + } } public async Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId) @@ -55,23 +63,29 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers if(id == default) throw new ArgumentException("id"); if(lastTweetPostedId == default) throw new ArgumentException("lastTweetPostedId"); if(lastTweetSynchronizedForAllFollowersId == default) throw new ArgumentException("lastTweetSynchronizedForAllFollowersId"); - - var query = $"UPDATE {_settings.TwitterUserTableName} SET lastTweetPostedId = {lastTweetPostedId}, lastTweetSynchronizedForAllFollowersId = {lastTweetSynchronizedForAllFollowersId} WHERE id = {id}"; + + var query = $"UPDATE {_settings.TwitterUserTableName} SET lastTweetPostedId = @lastTweetPostedId, lastTweetSynchronizedForAllFollowersId = @lastTweetSynchronizedForAllFollowersId WHERE id = @id"; using (var dbConnection = Connection) - using (var cmd = new NpgsqlCommand(query, dbConnection)) { dbConnection.Open(); - await cmd.ExecuteNonQueryAsync(); + + await dbConnection.QueryAsync(query, new { id, lastTweetPostedId, lastTweetSynchronizedForAllFollowersId }); } } - public Task DeleteTwitterUserAsync(string acct) + public async Task DeleteTwitterUserAsync(string acct) { + if (acct == default) throw new ArgumentException("acct"); + var query = $"DELETE FROM {_settings.TwitterUserTableName} WHERE acct = @acct"; + using (var dbConnection = Connection) + { + dbConnection.Open(); - throw new System.NotImplementedException(); + await dbConnection.QueryAsync(query, new { acct }); + } } } } \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs index 266a023..7150409 100644 --- a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/TwitterUserPostgresDalTests.cs @@ -95,5 +95,25 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers result = await dal.GetTwitterUserAsync(acct); Assert.IsNull(result); } + + [TestMethod] + public async Task GetAllTwitterUsers() + { + var dal = new TwitterUserPostgresDal(_settings); + for (var i = 0; i < 1000; i++) + { + var acct = $"myid{i}"; + var lastTweetId = 1548L; + + await dal.CreateTwitterUserAsync(acct, lastTweetId); + } + + var result = await dal.GetAllTwitterUsersAsync(); + Assert.AreEqual(1000, result.Length); + Assert.IsFalse(result[0].Id == default); + Assert.IsFalse(result[0].Acct == default); + Assert.IsFalse(result[0].LastTweetPostedId == default); + Assert.IsFalse(result[0].LastTweetSynchronizedForAllFollowersId == default); + } } } \ No newline at end of file From cdfb045c9063d5829ae321a7b113a5e20dd4300d Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 00:55:17 -0400 Subject: [PATCH 3/8] tweak follow table --- .../DataAccessLayers/DbInitializerPostgresDal.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs index cbe37d5..7b36644 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs @@ -106,8 +106,9 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers followings INTEGER[], followingsSyncStatus JSONB, - acct VARCHAR(20) UNIQUE, - host VARCHAR(20) + acct VARCHAR(50), + host VARCHAR(253), + UNIQUE (acct, host) );"; await _tools.ExecuteRequestAsync(createFollowers); From d846756a7f2f8370b02399641020963a90c4c0fc Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 00:55:56 -0400 Subject: [PATCH 4/8] added foolproofing --- .../DataAccessLayers/TwitterUserPostgresDal.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs index e02c6be..eaa610b 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs @@ -22,13 +22,15 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers public async Task CreateTwitterUserAsync(string acct, long lastTweetPostedId) { + acct = acct.ToLowerInvariant(); + using (var dbConnection = Connection) { dbConnection.Open(); await dbConnection.ExecuteAsync( $"INSERT INTO {_settings.TwitterUserTableName} (acct,lastTweetPostedId,lastTweetSynchronizedForAllFollowersId) VALUES(@acct,@lastTweetPostedId,@lastTweetSynchronizedForAllFollowersId)", - new { acct = acct, lastTweetPostedId = lastTweetPostedId, lastTweetSynchronizedForAllFollowersId = lastTweetPostedId }); + new { acct, lastTweetPostedId, lastTweetSynchronizedForAllFollowersId = lastTweetPostedId }); } } @@ -36,6 +38,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers { var query = $"SELECT * FROM {_settings.TwitterUserTableName} WHERE acct = @acct"; + acct = acct.ToLowerInvariant(); + using (var dbConnection = Connection) { dbConnection.Open(); @@ -78,6 +82,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers { if (acct == default) throw new ArgumentException("acct"); + acct = acct.ToLowerInvariant(); + var query = $"DELETE FROM {_settings.TwitterUserTableName} WHERE acct = @acct"; using (var dbConnection = Connection) From 490c68ccc5efc468303dc84f98aef2090b1f5f8d Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 00:56:26 -0400 Subject: [PATCH 5/8] implementation followers DAL + Tests --- .../DataAccessLayers/FollowersPostgresDal.cs | 139 ++++++++++- .../Contracts/IFollowersDal.cs | 13 +- .../BirdsiteLive.DAL/Models/Follower.cs | 10 +- .../FollowersPostgresDalTests.cs | 216 ++++++++++++++++++ 4 files changed, 369 insertions(+), 9 deletions(-) create mode 100644 src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs index ace1997..7e6c2ac 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs @@ -1,9 +1,142 @@ -using BirdsiteLive.DAL.Contracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Models; +using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base; +using BirdsiteLive.DAL.Postgres.Settings; +using Dapper; +using Newtonsoft.Json; namespace BirdsiteLive.DAL.Postgres.DataAccessLayers { - public class FollowersPostgresDal : IFollowersDal + public class FollowersPostgresDal : PostgresBase, IFollowersDal { - + #region Ctor + public FollowersPostgresDal(PostgresSettings settings) : base(settings) + { + + } + #endregion + + public async Task CreateFollowerAsync(string acct, string host, int[] followings, Dictionary followingSyncStatus) + { + var serializedDic = JsonConvert.SerializeObject(followingSyncStatus); + + acct = acct.ToLowerInvariant(); + host = host.ToLowerInvariant(); + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.ExecuteAsync( + $"INSERT INTO {_settings.FollowersTableName} (acct,host,followings,followingsSyncStatus) VALUES(@acct,@host,@followings, CAST(@followingsSyncStatus as json))", + new { acct, host, followings, followingsSyncStatus = serializedDic }); + } + } + + public async Task GetFollowerAsync(string acct, string host) + { + var query = $"SELECT * FROM {_settings.FollowersTableName} WHERE acct = @acct AND host = @host"; + + acct = acct.ToLowerInvariant(); + host = host.ToLowerInvariant(); + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + var result = (await dbConnection.QueryAsync(query, new { acct, host })).FirstOrDefault(); + return Convert(result); + } + } + + public async Task GetFollowersAsync(int followedUserId) + { + if (followedUserId == default) throw new ArgumentException("followedUserId"); + + var query = $"SELECT * FROM {_settings.FollowersTableName} WHERE @id=ANY(followings)"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + var result = await dbConnection.QueryAsync(query, new { id = followedUserId}); + return result.Select(Convert).ToArray(); + } + } + + public async Task UpdateFollowerAsync(int id, int[] followings, Dictionary followingsSyncStatus) + { + if (id == default) throw new ArgumentException("id"); + + var serializedDic = JsonConvert.SerializeObject(followingsSyncStatus); + var query = $"UPDATE {_settings.FollowersTableName} SET followings = @followings, followingsSyncStatus = CAST(@followingsSyncStatus as json) WHERE id = @id"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.QueryAsync(query, new { id, followings, followingsSyncStatus = serializedDic }); + } + } + + public async Task DeleteFollowerAsync(int id) + { + if (id == default) throw new ArgumentException("id"); + + var query = $"DELETE FROM {_settings.FollowersTableName} WHERE id = @id"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.QueryAsync(query, new { id }); + } + } + + public async Task DeleteFollowerAsync(string acct, string host) + { + if (acct == default) throw new ArgumentException("acct"); + if (host == default) throw new ArgumentException("host"); + + acct = acct.ToLowerInvariant(); + host = host.ToLowerInvariant(); + + var query = $"DELETE FROM {_settings.FollowersTableName} WHERE acct = @acct AND host = @host"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.QueryAsync(query, new { acct, host }); + } + } + + private Follower Convert(SerializedFollower follower) + { + if (follower == null) return null; + + return new Follower() + { + Id = follower.Id, + Acct = follower.Acct, + Host = follower.Host, + Followings = follower.Followings, + FollowingsSyncStatus = JsonConvert.DeserializeObject>(follower.FollowingsSyncStatus) + }; + } + } + + internal class SerializedFollower { + public int Id { get; set; } + + public int[] Followings { get; set; } + public string FollowingsSyncStatus { get; set; } + + public string Acct { get; set; } + public string Host { get; set; } } } \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs index ab8392d..92e0cb3 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs @@ -1,7 +1,16 @@ -namespace BirdsiteLive.DAL.Contracts +using System.Collections.Generic; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Models; + +namespace BirdsiteLive.DAL.Contracts { public interface IFollowersDal { - + Task GetFollowerAsync(string acct, string host); + Task CreateFollowerAsync(string acct, string host, int[] followings, Dictionary followingSyncStatus); + Task GetFollowersAsync(int followedUserId); + Task UpdateFollowerAsync(int id, int[] followings, Dictionary followingSyncStatus); + Task DeleteFollowerAsync(int id); + Task DeleteFollowerAsync(string acct, string host); } } \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs index a6c0903..5eedafb 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs @@ -1,13 +1,15 @@ -namespace BirdsiteLive.DAL.Models +using System.Collections.Generic; + +namespace BirdsiteLive.DAL.Models { public class Follower { public int Id { get; set; } - public int FollowingAccountId { get; set; } + public int[] Followings { get; set; } + public Dictionary FollowingsSyncStatus { get; set; } + public string Acct { get; set; } public string Host { get; set; } - - public long LastTweetSynchronizedId { get; set; } } } \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs new file mode 100644 index 0000000..f359f06 --- /dev/null +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Postgres.DataAccessLayers; +using BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers +{ + [TestClass] + public class FollowersPostgresDalTests : PostgresTestingBase + { + [TestInitialize] + public async Task TestInit() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + await dal.InitDbAsync(); + } + + [TestCleanup] + public async Task CleanUp() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + try + { + await dal.DeleteAllAsync(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + [TestMethod] + public async Task CreateAndGetFollower() + { + var acct = "myhandle"; + var host = "domain.ext"; + var following = new[] {12, 19, 23}; + var followingSync = new Dictionary() + { + {12, 165L}, + {19, 166L}, + {23, 167L} + }; + + var dal = new FollowersPostgresDal(_settings); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + + var result = await dal.GetFollowerAsync(acct, host); + + Assert.IsNotNull(result); + Assert.AreEqual(acct, result.Acct); + Assert.AreEqual(host, result.Host); + Assert.AreEqual(following.Length, result.Followings.Length); + 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 GetFollowersAsync() + { + var dal = new FollowersPostgresDal(_settings); + + //User 1 + var acct = "myhandle1"; + var host = "domain.ext"; + var following = new[] { 1,2,3 }; + var followingSync = new Dictionary(); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + + //User 2 + acct = "myhandle2"; + host = "domain.ext"; + following = new[] { 2, 4, 5 }; + await dal.CreateFollowerAsync(acct, host, following, followingSync); + + //User 2 + acct = "myhandle3"; + host = "domain.ext"; + following = new[] { 1 }; + await dal.CreateFollowerAsync(acct, host, following, followingSync); + + var result = await dal.GetFollowersAsync(2); + Assert.AreEqual(2, result.Length); + + result = await dal.GetFollowersAsync(5); + Assert.AreEqual(1, result.Length); + + result = await dal.GetFollowersAsync(24); + Assert.AreEqual(0, result.Length); + } + + [TestMethod] + public async Task CreateUpdateAndGetFollower_Add() + { + var acct = "myhandle"; + var host = "domain.ext"; + var following = new[] { 12, 19, 23 }; + var followingSync = new Dictionary() + { + {12, 165L}, + {19, 166L}, + {23, 167L} + }; + + var dal = new FollowersPostgresDal(_settings); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + var result = await dal.GetFollowerAsync(acct, host); + + var updatedFollowing = new[] { 12, 19, 23, 24 }; + var updatedFollowingSync = new Dictionary() + { + {12, 170L}, + {19, 171L}, + {23, 172L}, + {24, 173L} + }; + + await dal.UpdateFollowerAsync(result.Id, updatedFollowing, updatedFollowingSync); + result = await dal.GetFollowerAsync(acct, host); + + Assert.AreEqual(updatedFollowing.Length, result.Followings.Length); + Assert.AreEqual(updatedFollowing[0], result.Followings[0]); + Assert.AreEqual(updatedFollowingSync.Count, result.FollowingsSyncStatus.Count); + Assert.AreEqual(updatedFollowingSync.First().Key, result.FollowingsSyncStatus.First().Key); + Assert.AreEqual(updatedFollowingSync.First().Value, result.FollowingsSyncStatus.First().Value); + } + + [TestMethod] + public async Task CreateUpdateAndGetFollower_Remove() + { + var acct = "myhandle"; + var host = "domain.ext"; + var following = new[] { 12, 19, 23 }; + var followingSync = new Dictionary() + { + {12, 165L}, + {19, 166L}, + {23, 167L} + }; + + var dal = new FollowersPostgresDal(_settings); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + var result = await dal.GetFollowerAsync(acct, host); + + var updatedFollowing = new[] { 12, 19 }; + var updatedFollowingSync = new Dictionary() + { + {12, 170L}, + {19, 171L} + }; + + await dal.UpdateFollowerAsync(result.Id, updatedFollowing, updatedFollowingSync); + result = await dal.GetFollowerAsync(acct, host); + + Assert.AreEqual(updatedFollowing.Length, result.Followings.Length); + Assert.AreEqual(updatedFollowing[0], result.Followings[0]); + Assert.AreEqual(updatedFollowingSync.Count, result.FollowingsSyncStatus.Count); + Assert.AreEqual(updatedFollowingSync.First().Key, result.FollowingsSyncStatus.First().Key); + Assert.AreEqual(updatedFollowingSync.First().Value, result.FollowingsSyncStatus.First().Value); + } + + [TestMethod] + public async Task CreateAndDeleteFollower_ById() + { + var acct = "myhandle"; + var host = "domain.ext"; + var following = new[] { 12, 19, 23 }; + var followingSync = new Dictionary() + { + {12, 165L}, + {19, 166L}, + {23, 167L} + }; + + var dal = new FollowersPostgresDal(_settings); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + var result = await dal.GetFollowerAsync(acct, host); + Assert.IsNotNull(result); + + await dal.DeleteFollowerAsync(result.Id); + + result = await dal.GetFollowerAsync(acct, host); + Assert.IsNull(result); + } + + [TestMethod] + public async Task CreateAndDeleteFollower_ByHandle() + { + var acct = "myhandle"; + var host = "domain.ext"; + var following = new[] { 12, 19, 23 }; + var followingSync = new Dictionary() + { + {12, 165L}, + {19, 166L}, + {23, 167L} + }; + + var dal = new FollowersPostgresDal(_settings); + await dal.CreateFollowerAsync(acct, host, following, followingSync); + var result = await dal.GetFollowerAsync(acct, host); + Assert.IsNotNull(result); + + await dal.DeleteFollowerAsync(acct, host); + + result = await dal.GetFollowerAsync(acct, host); + Assert.IsNull(result); + } + } +} \ No newline at end of file From 99438fc43dc6808069a19ef9f1b2b8fcb44e8d8e Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 01:15:24 -0400 Subject: [PATCH 6/8] make tests more lisible --- .github/workflows/dotnet-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index c6b7818..bdfb537 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -26,5 +26,5 @@ jobs: run: dotnet build --configuration Release --no-restore working-directory: ${{env.working-directory}} - name: Test - run: dotnet test --no-restore --verbosity normal + run: dotnet test --no-restore --verbosity quiet working-directory: ${{env.working-directory}} From aa07ee880ebb9d8b444bccadf50858d9ba33a528 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 02:12:51 -0400 Subject: [PATCH 7/8] Implementation of CachedTweetsDal + Tests --- .../CachedTweetsPostgresDal.cs | 65 ++++++++++++-- .../Contracts/ICachedTweetsDal.cs | 5 +- .../BirdsiteLive.DAL/Models/CachedTweet.cs | 29 ++++++- .../CachedTweetsPostgresDalTests.cs | 87 +++++++++++++++++++ 4 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/CachedTweetsPostgresDalTests.cs diff --git a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs index 3251c41..b95d5d1 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/CachedTweetsPostgresDal.cs @@ -1,8 +1,13 @@ -using System.Threading.Tasks; +using System; +using System.Linq; +using System.Threading.Tasks; using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Models; using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base; using BirdsiteLive.DAL.Postgres.Settings; using BirdsiteLive.DAL.Postgres.Tools; +using Dapper; +using Newtonsoft.Json; using Tweetinvi.Models; namespace BirdsiteLive.DAL.Postgres.DataAccessLayers @@ -10,25 +15,69 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers public class CachedTweetsPostgresDal : PostgresBase, ICachedTweetsDal { #region Ctor - public CachedTweetsPostgresDal(PostgresSettings settings, PostgresTools tools) : base(settings) + public CachedTweetsPostgresDal(PostgresSettings settings) : base(settings) { } #endregion - public Task AddTweetAsync(long tweetId, int userId, ITweet tweet) + public async Task CreateTweetAsync(long tweetId, int userId, CachedTweet tweet) { - throw new System.NotImplementedException(); + if(tweetId == default) throw new ArgumentException("tweetId"); + if(userId == default) throw new ArgumentException("userId"); + + var serializedData = JsonConvert.SerializeObject(tweet); + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.ExecuteAsync( + $"INSERT INTO {_settings.CachedTweetsTableName} (id,twitterUserId,data) VALUES(@id,@twitterUserId,CAST(@data as json))", + new { id = tweetId, twitterUserId = userId, data = serializedData }); + } } - public Task GetTweetAsync(long tweetId) + public async Task GetTweetAsync(long tweetId) { - throw new System.NotImplementedException(); + if (tweetId == default) throw new ArgumentException("tweetId"); + + var query = $"SELECT * FROM {_settings.CachedTweetsTableName} WHERE id = @id"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + var result = (await dbConnection.QueryAsync(query, new { id = tweetId })).FirstOrDefault(); + return Convert(result); + } } - public Task DeleteTweetAsync(long tweetId) + public async Task DeleteTweetAsync(long tweetId) { - throw new System.NotImplementedException(); + if (tweetId == default) throw new ArgumentException("tweetId"); + + var query = $"DELETE FROM {_settings.CachedTweetsTableName} WHERE id = @id"; + + using (var dbConnection = Connection) + { + dbConnection.Open(); + + await dbConnection.QueryAsync(query, new { id = tweetId }); + } + } + + private CachedTweet Convert(SerializedTweet result) + { + if (result == null || string.IsNullOrWhiteSpace(result.Data)) return null; + return JsonConvert.DeserializeObject(result.Data); } } + + internal class SerializedTweet + { + public long Id { get; set; } + public int TwitterUserId { get; set; } + public string Data { get; set; } + } } \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs index d1214a5..64ffe41 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ICachedTweetsDal.cs @@ -1,12 +1,13 @@ using System.Threading.Tasks; +using BirdsiteLive.DAL.Models; using Tweetinvi.Models; namespace BirdsiteLive.DAL.Contracts { public interface ICachedTweetsDal { - Task AddTweetAsync(long tweetId, int userId, ITweet tweet); - Task GetTweetAsync(long tweetId); + Task CreateTweetAsync(long tweetId, int userId, CachedTweet tweet); + Task GetTweetAsync(long tweetId); Task DeleteTweetAsync(long tweetId); } } \ No newline at end of file diff --git a/src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs b/src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs index d1472b6..25f5120 100644 --- a/src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs +++ b/src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs @@ -1,10 +1,31 @@ -namespace BirdsiteLive.DAL.Models +using System; + +namespace BirdsiteLive.DAL.Models { public class CachedTweet { + public int UserId { get; set; } + public long Id { get; set; } - public long TwitterUserId { get; set; } - - public string TweetData { get; set; } + public DateTime CreatedAt { get; set; } + + public string Text { get; set; } + public string FullText { get; set; } + + public long? InReplyToStatusId { get; set; } + public string InReplyToStatusIdStr { get; set; } + public long? InReplyToUserId { get; set; } + public string InReplyToUserIdStr { get; set; } + public string InReplyToScreenName { get; set; } + + // List Hashtags { get; } + //List Urls { get; } + //List Media { get; } + //List UserMentions { get; } + //List Retweets { get; set; } + public bool IsRetweet { get; } + public CachedTweet RetweetedTweet { get; } + public string Url { get; } } + } \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/CachedTweetsPostgresDalTests.cs b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/CachedTweetsPostgresDalTests.cs new file mode 100644 index 0000000..021784e --- /dev/null +++ b/src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/CachedTweetsPostgresDalTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Models; +using BirdsiteLive.DAL.Postgres.DataAccessLayers; +using BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers +{ + [TestClass] + public class CachedTweetsPostgresDalTests : PostgresTestingBase + { + [TestInitialize] + public async Task TestInit() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + await dal.InitDbAsync(); + } + + [TestCleanup] + public async Task CleanUp() + { + var dal = new DbInitializerPostgresDal(_settings, _tools); + try + { + await dal.DeleteAllAsync(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + [TestMethod] + public async Task CreateAndGet() + { + var id = 152L; + var userId = 15; + + var tweet = new CachedTweet + { + UserId = userId, + Id = id, + Text = "text data", + FullText = "full text data", + CreatedAt = DateTime.UtcNow + }; + + var dal = new CachedTweetsPostgresDal(_settings); + await dal.CreateTweetAsync(id, userId, tweet); + + var result = await dal.GetTweetAsync(id); + + Assert.IsNotNull(result); + Assert.AreEqual(id, result.Id); + Assert.AreEqual(tweet.Text, result.Text); + Assert.AreEqual(tweet.FullText, result.FullText); + Assert.AreEqual(tweet.CreatedAt, result.CreatedAt); + } + + [TestMethod] + public async Task CreateAndDelete() + { + var id = 152L; + var userId = 15; + + var tweet = new CachedTweet + { + UserId = userId, + Id = id, + Text = "text data", + FullText = "full text data", + CreatedAt = DateTime.UtcNow + }; + + var dal = new CachedTweetsPostgresDal(_settings); + await dal.CreateTweetAsync(id, userId, tweet); + + var result = await dal.GetTweetAsync(id); + Assert.IsNotNull(result); + + await dal.DeleteTweetAsync(id); + result = await dal.GetTweetAsync(id); + Assert.IsNull(result); + } + } +} \ No newline at end of file From 4457f2c30f6a7996ab37b8b52909271473678497 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Mon, 6 Jul 2020 21:25:10 -0400 Subject: [PATCH 8/8] added nodeinfo data --- .../Controllers/WellKnownController.cs | 119 +++++++++++++++--- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/src/BirdsiteLive/Controllers/WellKnownController.cs b/src/BirdsiteLive/Controllers/WellKnownController.cs index 89c7f7c..613b948 100644 --- a/src/BirdsiteLive/Controllers/WellKnownController.cs +++ b/src/BirdsiteLive/Controllers/WellKnownController.cs @@ -24,6 +24,52 @@ namespace BirdsiteLive.Controllers } #endregion + [Route("/.well-known/nodeinfo")] + public IActionResult WellKnownNodeInfo() + { + var nodeInfo = new WellKnownNodeInfo + { + links = new Link[] + { + new Link() + { + rel = "http://nodeinfo.diaspora.software/ns/schema/2.0", + href = $"https://{_settings.Domain}/nodeinfo/2.0.json" + } + } + }; + return new JsonResult(nodeInfo); + } + + [Route("/nodeinfo/2.0.json")] + public IActionResult NodeInfo() + { + var nodeInfo = new NodeInfo + { + version = "2.0", + usage = new Usage() + { + localPosts = 0, + users = new Users() + { + total = 0 + } + }, + software = new Software() + { + name = "BirdsiteLive", + version = "0.1.0" + }, + protocols = new [] + { + "activitypub" + }, + openRegistrations = false + }; + + return new JsonResult(nodeInfo); + } + [Route("/.well-known/webfinger")] public IActionResult Webfinger(string resource = null) { @@ -51,7 +97,7 @@ namespace BirdsiteLive.Controllers if (!string.IsNullOrWhiteSpace(domain) && domain != _settings.Domain) return NotFound(); - + var user = _twitterService.GetUser(name); if (user == null) return NotFound(); @@ -59,7 +105,7 @@ namespace BirdsiteLive.Controllers var result = new WebFingerResult() { subject = $"acct:{name}@{_settings.Domain}", - aliases = new [] + aliases = new[] { $"https://{_settings.Domain}/@{name}", $"https://{_settings.Domain}/users/{name}" @@ -83,19 +129,64 @@ namespace BirdsiteLive.Controllers return new JsonResult(result); } + } - public class WebFingerResult - { - public string subject { get; set; } - public string[] aliases { get; set; } - public List links { get; set; } = new List(); - } + public class WebFingerResult + { + public string subject { get; set; } + public string[] aliases { get; set; } + public List links { get; set; } = new List(); + } - public class WebFingerLink - { - public string rel { get; set; } - public string type { get; set; } - public string href { get; set; } - } + public class WebFingerLink + { + public string rel { get; set; } + public string type { get; set; } + public string href { get; set; } + } + + public class WellKnownNodeInfo + { + public Link[] links { get; set; } + } + + public class Link + { + public string href { get; set; } + public string rel { get; set; } + } + + public class NodeInfo + { + public string version { get; set; } + public string[] protocols { get; set; } + public Software software { get; set; } + public Usage usage { get; set; } + public bool openRegistrations { get; set; } + public Services services { get; set; } + public object metadata { get; set; } + } + + public class Services + { + public object[] inbound { get; set; } + public object[] outbound { get; set; } + } + + public class Software + { + public string name { get; set; } + public string version { get; set; } + } + + public class Usage + { + public int localPosts { get; set; } + public Users users { get; set; } + } + + public class Users + { + public int total { get; set; } } } \ No newline at end of file