Merge branch 'master' of https://git.sr.ht/~cloutier/bird.makeup into makeup
This commit is contained in:
commit
881643be84
61 changed files with 165 additions and 1502 deletions
25
README.md
25
README.md
|
@ -8,25 +8,26 @@ Bird.makeup is a way to follow Twitter users from any ActivityPub service. The a
|
|||
|
||||
Compared to BirdsiteLive, bird.makeup is:
|
||||
|
||||
More scalable:
|
||||
- Twitter API calls are not rate-limited
|
||||
- It is possible to split the Twitter crawling to multiple servers
|
||||
- There are now integration tests for the non-official api
|
||||
- The core pipeline has been tweaked to remove bottlenecks. As of writing this, bird.makeup supports without problems more than 20k users.
|
||||
- Twitter users with no followers on the fediverse will stop being fetched
|
||||
|
||||
More native to the fediverse:
|
||||
- Retweets are propagated as boosts
|
||||
- Activities are now "unlisted" which means that they won't polute the public timeline, but they can still be boosted
|
||||
- WIP support for QT
|
||||
|
||||
More modern:
|
||||
- Moved from .net core 3.1 to .net 6 which is still supported
|
||||
- Moved from postgres 9 to 15
|
||||
- Moved from Newtonsoft.Json to System.Text.Json
|
||||
|
||||
More scalable:
|
||||
- Twitter API calls are not rate-limited
|
||||
- There are now integration tests for the non-official api
|
||||
- The core pipeline has been tweaked to remove bottlenecks. As of writing this, bird.makeup supports without problems more than 10k users.
|
||||
- Twitter users with no followers on the fediverse will stop being fetched
|
||||
|
||||
More native to the fediverse:
|
||||
- Retweets are propagated as boosts
|
||||
- Activities are now "unlisted" which means that they won't polute the public timeline
|
||||
- WIP support for QT
|
||||
|
||||
## Official instance
|
||||
|
||||
You can find an official instance here: [bird.makeup](https://bird.makeup). If you are an instance admin that prefers to not have tweets federated to you, please block the entire instance.
|
||||
You can find the official instance here: [bird.makeup](https://bird.makeup). If you are an instance admin that prefers to not have tweets federated to you, please block the entire instance.
|
||||
|
||||
Please consider if you really need another instance before spinning up a new one, as having multiple domain makes it harder for moderators to block twitter content.
|
||||
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BirdsiteLive.DAL.Models;
|
||||
using BirdsiteLive.Moderation.Actions;
|
||||
using BSLManager.Domain;
|
||||
using BSLManager.Tools;
|
||||
using Terminal.Gui;
|
||||
|
||||
namespace BSLManager
|
||||
{
|
||||
public class App
|
||||
{
|
||||
private readonly IFollowersDal _followersDal;
|
||||
private readonly IRemoveFollowerAction _removeFollowerAction;
|
||||
|
||||
private readonly FollowersListState _state = new FollowersListState();
|
||||
|
||||
#region Ctor
|
||||
public App(IFollowersDal followersDal, IRemoveFollowerAction removeFollowerAction)
|
||||
{
|
||||
_followersDal = followersDal;
|
||||
_removeFollowerAction = removeFollowerAction;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Run()
|
||||
{
|
||||
Application.Init();
|
||||
var top = Application.Top;
|
||||
|
||||
// Creates the top-level window to show
|
||||
var win = new Window("BSL Manager")
|
||||
{
|
||||
X = 0,
|
||||
Y = 1, // Leave one row for the toplevel menu
|
||||
|
||||
// By using Dim.Fill(), it will automatically resize without manual intervention
|
||||
Width = Dim.Fill(),
|
||||
Height = Dim.Fill()
|
||||
};
|
||||
|
||||
top.Add(win);
|
||||
|
||||
// Creates a menubar, the item "New" has a help menu.
|
||||
var menu = new MenuBar(new MenuBarItem[]
|
||||
{
|
||||
new MenuBarItem("_File", new MenuItem[]
|
||||
{
|
||||
new MenuItem("_Quit", "", () =>
|
||||
{
|
||||
if (Quit()) top.Running = false;
|
||||
})
|
||||
}),
|
||||
//new MenuBarItem ("_Edit", new MenuItem [] {
|
||||
// new MenuItem ("_Copy", "", null),
|
||||
// new MenuItem ("C_ut", "", null),
|
||||
// new MenuItem ("_Paste", "", null)
|
||||
//})
|
||||
});
|
||||
top.Add(menu);
|
||||
|
||||
static bool Quit()
|
||||
{
|
||||
var n = MessageBox.Query(50, 7, "Quit BSL Manager", "Are you sure you want to quit?", "Yes", "No");
|
||||
return n == 0;
|
||||
}
|
||||
|
||||
RetrieveUserList();
|
||||
|
||||
var list = new ListView(_state.GetDisplayableList())
|
||||
{
|
||||
X = 1,
|
||||
Y = 3,
|
||||
Width = Dim.Fill(),
|
||||
Height = Dim.Fill()
|
||||
};
|
||||
|
||||
list.KeyDown += _ =>
|
||||
{
|
||||
if (_.KeyEvent.Key == Key.Enter)
|
||||
{
|
||||
OpenFollowerDialog(list.SelectedItem);
|
||||
}
|
||||
else if (_.KeyEvent.Key == Key.Delete
|
||||
|| _.KeyEvent.Key == Key.DeleteChar
|
||||
|| _.KeyEvent.Key == Key.Backspace
|
||||
|| _.KeyEvent.Key == Key.D)
|
||||
{
|
||||
OpenDeleteDialog(list.SelectedItem);
|
||||
}
|
||||
};
|
||||
|
||||
var listingFollowersLabel = new Label(1, 0, "Listing followers");
|
||||
var filterLabel = new Label("Filter: ") { X = 1, Y = 1 };
|
||||
var filterText = new TextField("")
|
||||
{
|
||||
X = Pos.Right(filterLabel),
|
||||
Y = 1,
|
||||
Width = 40
|
||||
};
|
||||
|
||||
filterText.KeyDown += _ =>
|
||||
{
|
||||
var text = filterText.Text.ToString();
|
||||
if (_.KeyEvent.Key == Key.Enter && !string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
_state.FilterBy(text);
|
||||
ConsoleGui.RefreshUI();
|
||||
}
|
||||
};
|
||||
|
||||
win.Add(
|
||||
listingFollowersLabel,
|
||||
filterLabel,
|
||||
filterText,
|
||||
list
|
||||
);
|
||||
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
private void OpenFollowerDialog(int selectedIndex)
|
||||
{
|
||||
var close = new Button(3, 14, "Close");
|
||||
close.Clicked += () => Application.RequestStop();
|
||||
|
||||
var dialog = new Dialog("Info", 60, 18, close);
|
||||
|
||||
var follower = _state.GetElementAt(selectedIndex);
|
||||
|
||||
var name = new Label($"User: @{follower.Acct}@{follower.Host}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
var following = new Label($"Following Count: {follower.Followings.Count}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 3,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
var errors = new Label($"Posting Errors: {follower.PostingErrorCount}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 4,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
var inbox = new Label($"Inbox: {follower.InboxRoute}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 5,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
var sharedInbox = new Label($"Shared Inbox: {follower.SharedInboxRoute}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 6,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
|
||||
dialog.Add(name);
|
||||
dialog.Add(following);
|
||||
dialog.Add(errors);
|
||||
dialog.Add(inbox);
|
||||
dialog.Add(sharedInbox);
|
||||
dialog.Add(close);
|
||||
Application.Run(dialog);
|
||||
}
|
||||
|
||||
private void OpenDeleteDialog(int selectedIndex)
|
||||
{
|
||||
bool okpressed = false;
|
||||
var ok = new Button(10, 14, "Yes");
|
||||
ok.Clicked += () =>
|
||||
{
|
||||
Application.RequestStop();
|
||||
okpressed = true;
|
||||
};
|
||||
|
||||
var cancel = new Button(3, 14, "No");
|
||||
cancel.Clicked += () => Application.RequestStop();
|
||||
|
||||
var dialog = new Dialog("Delete", 60, 18, cancel, ok);
|
||||
|
||||
var follower = _state.GetElementAt(selectedIndex);
|
||||
var name = new Label($"User: @{follower.Acct}@{follower.Host}")
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
var entry = new Label("Delete user and remove all their followings?")
|
||||
{
|
||||
X = 1,
|
||||
Y = 3,
|
||||
Width = Dim.Fill(),
|
||||
Height = 1
|
||||
};
|
||||
dialog.Add(name);
|
||||
dialog.Add(entry);
|
||||
Application.Run(dialog);
|
||||
|
||||
if (okpressed)
|
||||
{
|
||||
DeleteAndRemoveUser(selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteAndRemoveUser(int el)
|
||||
{
|
||||
Application.MainLoop.Invoke(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var userToDelete = _state.GetElementAt(el);
|
||||
|
||||
BasicLogger.Log($"Delete {userToDelete.Acct}@{userToDelete.Host}");
|
||||
await _removeFollowerAction.ProcessAsync(userToDelete);
|
||||
BasicLogger.Log($"Remove user from list");
|
||||
_state.RemoveAt(el);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BasicLogger.Log(e.Message);
|
||||
}
|
||||
|
||||
ConsoleGui.RefreshUI();
|
||||
});
|
||||
}
|
||||
|
||||
private void RetrieveUserList()
|
||||
{
|
||||
Application.MainLoop.Invoke(async () =>
|
||||
{
|
||||
var followers = await _followersDal.GetAllFollowersAsync();
|
||||
_state.Load(followers.ToList());
|
||||
ConsoleGui.RefreshUI();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lamar" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||
<PackageReference Include="Terminal.Gui" Version="1.0.0-beta.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BirdsiteLive.Common\BirdsiteLive.Common.csproj" />
|
||||
<ProjectReference Include="..\BirdsiteLive.Moderation\BirdsiteLive.Moderation.csproj" />
|
||||
<ProjectReference Include="..\DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="key.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,94 +0,0 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
using BirdsiteLive.Common.Structs;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BirdsiteLive.DAL.Postgres.DataAccessLayers;
|
||||
using BirdsiteLive.DAL.Postgres.Settings;
|
||||
using Lamar;
|
||||
using Lamar.Scanning.Conventions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BSLManager
|
||||
{
|
||||
public class Bootstrapper
|
||||
{
|
||||
private readonly DbSettings _dbSettings;
|
||||
private readonly InstanceSettings _instanceSettings;
|
||||
|
||||
#region Ctor
|
||||
public Bootstrapper(DbSettings dbSettings, InstanceSettings instanceSettings)
|
||||
{
|
||||
_dbSettings = dbSettings;
|
||||
_instanceSettings = instanceSettings;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Container Init()
|
||||
{
|
||||
var container = new Container(x =>
|
||||
{
|
||||
x.For<DbSettings>().Use(x => _dbSettings);
|
||||
|
||||
x.For<InstanceSettings>().Use(x => _instanceSettings);
|
||||
|
||||
if (string.Equals(_dbSettings.Type, DbTypes.Postgres, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var connString = $"Host={_dbSettings.Host};Username={_dbSettings.User};Password={_dbSettings.Password};Database={_dbSettings.Name}";
|
||||
var postgresSettings = new PostgresSettings
|
||||
{
|
||||
ConnString = connString
|
||||
};
|
||||
x.For<PostgresSettings>().Use(x => postgresSettings);
|
||||
|
||||
x.For<ITwitterUserDal>().Use<TwitterUserPostgresDal>().Singleton();
|
||||
x.For<IFollowersDal>().Use<FollowersPostgresDal>().Singleton();
|
||||
x.For<IDbInitializerDal>().Use<DbInitializerPostgresDal>().Singleton();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{_dbSettings.Type} is not supported");
|
||||
}
|
||||
|
||||
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
|
||||
x.For<IHttpClientFactory>().Use(_ => serviceProvider.GetService<IHttpClientFactory>());
|
||||
|
||||
x.For(typeof(ILogger<>)).Use(typeof(DummyLogger<>));
|
||||
|
||||
x.Scan(_ =>
|
||||
{
|
||||
_.Assembly("BirdsiteLive.Twitter");
|
||||
_.Assembly("BirdsiteLive.Domain");
|
||||
_.Assembly("BirdsiteLive.DAL");
|
||||
_.Assembly("BirdsiteLive.DAL.Postgres");
|
||||
_.Assembly("BirdsiteLive.Moderation");
|
||||
|
||||
_.TheCallingAssembly();
|
||||
|
||||
_.WithDefaultConventions();
|
||||
|
||||
_.LookForRegistries();
|
||||
});
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
public class DummyLogger<T> : ILogger<T>
|
||||
{
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BirdsiteLive.DAL.Models;
|
||||
|
||||
namespace BSLManager.Domain
|
||||
{
|
||||
public class FollowersListState
|
||||
{
|
||||
private readonly List<string> _filteredDisplayableUserList = new List<string>();
|
||||
|
||||
private List<Follower> _sourceUserList = new List<Follower>();
|
||||
private List<Follower> _filteredSourceUserList = new List<Follower>();
|
||||
|
||||
public void Load(List<Follower> followers)
|
||||
{
|
||||
_sourceUserList = followers.OrderByDescending(x => x.Followings.Count).ToList();
|
||||
|
||||
ResetLists();
|
||||
}
|
||||
|
||||
private void ResetLists()
|
||||
{
|
||||
_filteredSourceUserList = _sourceUserList.ToList();
|
||||
|
||||
_filteredDisplayableUserList.Clear();
|
||||
|
||||
foreach (var follower in _sourceUserList)
|
||||
{
|
||||
var displayedUser = $"{GetFullHandle(follower)} ({follower.Followings.Count}) (err:{follower.PostingErrorCount})";
|
||||
_filteredDisplayableUserList.Add(displayedUser);
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetDisplayableList()
|
||||
{
|
||||
return _filteredDisplayableUserList;
|
||||
}
|
||||
|
||||
public void FilterBy(string pattern)
|
||||
{
|
||||
ResetLists();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pattern))
|
||||
{
|
||||
var elToRemove = _filteredSourceUserList
|
||||
.Where(x => !GetFullHandle(x).Contains(pattern))
|
||||
.Select(x => x)
|
||||
.ToList();
|
||||
|
||||
foreach (var el in elToRemove)
|
||||
{
|
||||
_filteredSourceUserList.Remove(el);
|
||||
|
||||
var dElToRemove = _filteredDisplayableUserList.First(x => x.Contains(GetFullHandle(el)));
|
||||
_filteredDisplayableUserList.Remove(dElToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullHandle(Follower follower)
|
||||
{
|
||||
return $"@{follower.Acct}@{follower.Host}";
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
var displayableUser = _filteredDisplayableUserList[index];
|
||||
var sourceUser = _filteredSourceUserList[index];
|
||||
|
||||
_filteredDisplayableUserList.Remove(displayableUser);
|
||||
|
||||
_filteredSourceUserList.Remove(sourceUser);
|
||||
_sourceUserList.Remove(sourceUser);
|
||||
}
|
||||
|
||||
public Follower GetElementAt(int index)
|
||||
{
|
||||
return _filteredSourceUserList[index];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BSLManager.Tools;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NStack;
|
||||
using Terminal.Gui;
|
||||
|
||||
namespace BSLManager
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
Console.OutputEncoding = Encoding.Default;
|
||||
|
||||
var settingsManager = new SettingsManager();
|
||||
var settings = settingsManager.GetSettings();
|
||||
|
||||
//var builder = new ConfigurationBuilder()
|
||||
// .AddEnvironmentVariables();
|
||||
//var configuration = builder.Build();
|
||||
|
||||
//var dbSettings = configuration.GetSection("Db").Get<DbSettings>();
|
||||
//var instanceSettings = configuration.GetSection("Instance").Get<InstanceSettings>();
|
||||
|
||||
var bootstrapper = new Bootstrapper(settings.dbSettings, settings.instanceSettings);
|
||||
var container = bootstrapper.Init();
|
||||
|
||||
var app = container.GetInstance<App>();
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BSLManager.Tools
|
||||
{
|
||||
public static class BasicLogger
|
||||
{
|
||||
public static void Log(string log)
|
||||
{
|
||||
File.AppendAllLines($"Log-{Guid.NewGuid()}.txt", new []{ log });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
using System.Reflection;
|
||||
using Terminal.Gui;
|
||||
|
||||
namespace BSLManager.Tools
|
||||
{
|
||||
public static class ConsoleGui
|
||||
{
|
||||
public static void RefreshUI()
|
||||
{
|
||||
typeof(Application)
|
||||
.GetMethod("TerminalResized", BindingFlags.Static | BindingFlags.NonPublic)
|
||||
.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
|
||||
namespace BSLManager.Tools
|
||||
{
|
||||
public class SettingsManager
|
||||
{
|
||||
private const string LocalFileName = "ManagerSettings.json";
|
||||
|
||||
public (DbSettings dbSettings, InstanceSettings instanceSettings) GetSettings()
|
||||
{
|
||||
var localSettingsData = GetLocalSettingsFile();
|
||||
if (localSettingsData != null) return Convert(localSettingsData);
|
||||
|
||||
Console.WriteLine("We need to set up the manager");
|
||||
Console.WriteLine("Please provide the following information as provided in the docker-compose file");
|
||||
|
||||
LocalSettingsData data;
|
||||
do
|
||||
{
|
||||
data = GetDataFromUser();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Please check if all is ok:");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Db Host: {data.DbHost}");
|
||||
Console.WriteLine($"Db Name: {data.DbName}");
|
||||
Console.WriteLine($"Db User: {data.DbUser}");
|
||||
Console.WriteLine($"Db Password: {data.DbPassword}");
|
||||
Console.WriteLine($"Instance Domain: {data.InstanceDomain}");
|
||||
Console.WriteLine();
|
||||
|
||||
string resp;
|
||||
do
|
||||
{
|
||||
Console.WriteLine("Is it valid? (yes, no)");
|
||||
resp = Console.ReadLine()?.Trim().ToLowerInvariant();
|
||||
|
||||
if (resp == "n" || resp == "no") data = null;
|
||||
|
||||
} while (resp != "y" && resp != "yes" && resp != "n" && resp != "no");
|
||||
|
||||
} while (data == null);
|
||||
|
||||
SaveLocalSettings(data);
|
||||
return Convert(data);
|
||||
}
|
||||
|
||||
private LocalSettingsData GetDataFromUser()
|
||||
{
|
||||
var data = new LocalSettingsData();
|
||||
|
||||
Console.WriteLine("Db Host:");
|
||||
data.DbHost = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Db Name:");
|
||||
data.DbName = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Db User:");
|
||||
data.DbUser = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Db Password:");
|
||||
data.DbPassword = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Instance Domain:");
|
||||
data.InstanceDomain = Console.ReadLine();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private (DbSettings dbSettings, InstanceSettings instanceSettings) Convert(LocalSettingsData data)
|
||||
{
|
||||
var dbSettings = new DbSettings
|
||||
{
|
||||
Type = data.DbType,
|
||||
Host = data.DbHost,
|
||||
Name = data.DbName,
|
||||
User = data.DbUser,
|
||||
Password = data.DbPassword
|
||||
};
|
||||
var instancesSettings = new InstanceSettings
|
||||
{
|
||||
Domain = data.InstanceDomain
|
||||
};
|
||||
return (dbSettings, instancesSettings);
|
||||
}
|
||||
|
||||
private LocalSettingsData GetLocalSettingsFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(LocalFileName)) return null;
|
||||
|
||||
var jsonContent = File.ReadAllText(LocalFileName);
|
||||
var content = JsonSerializer.Deserialize<LocalSettingsData>(jsonContent);
|
||||
return content;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveLocalSettings(LocalSettingsData data)
|
||||
{
|
||||
var jsonContent = JsonSerializer.Serialize(data);
|
||||
File.WriteAllText(LocalFileName, jsonContent);
|
||||
}
|
||||
}
|
||||
|
||||
internal class LocalSettingsData
|
||||
{
|
||||
public string DbType { get; set; } = "postgres";
|
||||
public string DbHost { get; set; }
|
||||
public string DbName { get; set; }
|
||||
public string DbUser { get; set; }
|
||||
public string DbPassword { get; set; }
|
||||
|
||||
public string InstanceDomain { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -42,9 +42,6 @@ namespace BirdsiteLive.Domain.BusinessUseCases
|
|||
var twitterUserId = twitterUser.Id;
|
||||
if(!follower.Followings.Contains(twitterUserId))
|
||||
follower.Followings.Add(twitterUserId);
|
||||
|
||||
if(!follower.FollowingsSyncStatus.ContainsKey(twitterUserId))
|
||||
follower.FollowingsSyncStatus.Add(twitterUserId, -1);
|
||||
|
||||
// Save Follower
|
||||
await _followerDal.UpdateFollowerAsync(follower);
|
||||
|
|
|
@ -36,9 +36,6 @@ namespace BirdsiteLive.Domain.BusinessUseCases
|
|||
if (follower.Followings.Contains(twitterUserId))
|
||||
follower.Followings.Remove(twitterUserId);
|
||||
|
||||
if (follower.FollowingsSyncStatus.ContainsKey(twitterUserId))
|
||||
follower.FollowingsSyncStatus.Remove(twitterUserId);
|
||||
|
||||
// Save or delete Follower
|
||||
if (follower.Followings.Any())
|
||||
await _followerDal.UpdateFollowerAsync(follower);
|
||||
|
|
|
@ -41,9 +41,6 @@ namespace BirdsiteLive.Moderation.Actions
|
|||
if (follower.Followings.Contains(twitterUserId))
|
||||
follower.Followings.Remove(twitterUserId);
|
||||
|
||||
if (follower.FollowingsSyncStatus.ContainsKey(twitterUserId))
|
||||
follower.FollowingsSyncStatus.Remove(twitterUserId);
|
||||
|
||||
if (follower.Followings.Any())
|
||||
await _followersDal.UpdateFollowerAsync(follower);
|
||||
else
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.Pipeline.Models;
|
||||
|
||||
namespace BirdsiteLive.Pipeline.Contracts
|
||||
{
|
||||
public interface ISaveProgressionTask
|
||||
{
|
||||
Task ProcessAsync(UserWithDataToSync userWithTweetsToSync, CancellationToken ct);
|
||||
}
|
||||
}
|
|
@ -61,25 +61,25 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
|
|||
{
|
||||
// skip the first time to avoid sending backlog of tweet
|
||||
var tweetId = tweets.Last().Id;
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, tweetId, tweetId, user.FetchingErrorCount, now);
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, tweetId, user.FetchingErrorCount, now);
|
||||
}
|
||||
else if (tweets.Length > 0 && user.LastTweetPostedId != -1)
|
||||
{
|
||||
userWtData.Tweets = tweets;
|
||||
usersWtTweets.Add(userWtData);
|
||||
var tweetId = tweets.Last().Id;
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, tweetId, tweetId, user.FetchingErrorCount, now);
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, tweetId, user.FetchingErrorCount, now);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.LastTweetSynchronizedForAllFollowersId, user.FetchingErrorCount, now);
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.FetchingErrorCount, now);
|
||||
}
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
_logger.LogError(e.Message);
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.LastTweetSynchronizedForAllFollowersId, user.FetchingErrorCount, now);
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.FetchingErrorCount, now);
|
||||
}
|
||||
});
|
||||
todo.Add(t);
|
||||
|
@ -104,7 +104,7 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
|
|||
if (user.LastTweetPostedId == -1)
|
||||
tweets = await _twitterTweetsService.GetTimelineAsync(user.Acct);
|
||||
else
|
||||
tweets = await _twitterTweetsService.GetTimelineAsync(user.Acct, user.LastTweetSynchronizedForAllFollowersId);
|
||||
tweets = await _twitterTweetsService.GetTimelineAsync(user.Acct, user.LastTweetPostedId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BirdsiteLive.Pipeline.Contracts;
|
||||
using BirdsiteLive.Pipeline.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BirdsiteLive.Pipeline.Processors.SubTasks
|
||||
{
|
||||
public class SaveProgressionTask : ISaveProgressionTask
|
||||
{
|
||||
private readonly ITwitterUserDal _twitterUserDal;
|
||||
private readonly ILogger<SaveProgressionTask> _logger;
|
||||
|
||||
#region Ctor
|
||||
public SaveProgressionTask(ITwitterUserDal twitterUserDal, ILogger<SaveProgressionTask> logger)
|
||||
{
|
||||
_twitterUserDal = twitterUserDal;
|
||||
_logger = logger;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public async Task ProcessAsync(UserWithDataToSync userWithTweetsToSync, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (userWithTweetsToSync.Tweets.Length == 0)
|
||||
{
|
||||
_logger.LogWarning("No tweets synchronized");
|
||||
return;
|
||||
}
|
||||
if(userWithTweetsToSync.Followers.Length == 0)
|
||||
{
|
||||
_logger.LogWarning("No Followers found for {User}", userWithTweetsToSync.User.Acct);
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = userWithTweetsToSync.User.Id;
|
||||
var followingSyncStatuses = userWithTweetsToSync.Followers.Select(x => x.FollowingsSyncStatus[userId]).ToList();
|
||||
|
||||
if (followingSyncStatuses.Count == 0)
|
||||
{
|
||||
_logger.LogWarning("No Followers sync found for {User}, Id: {UserId}", userWithTweetsToSync.User.Acct, userId);
|
||||
return;
|
||||
}
|
||||
|
||||
var lastPostedTweet = userWithTweetsToSync.Tweets.Select(x => x.Id).Max();
|
||||
var minimumSync = followingSyncStatuses.Min();
|
||||
var now = DateTime.UtcNow;
|
||||
await _twitterUserDal.UpdateTwitterUserAsync(userId, lastPostedTweet, minimumSync, userWithTweetsToSync.User.FetchingErrorCount, now);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "SaveProgressionProcessor.ProcessAsync() Exception");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,16 +21,14 @@ namespace BirdsiteLive.Pipeline
|
|||
private readonly IRetrieveTweetsProcessor _retrieveTweetsProcessor;
|
||||
private readonly IRetrieveFollowersProcessor _retrieveFollowersProcessor;
|
||||
private readonly ISendTweetsToFollowersProcessor _sendTweetsToFollowersProcessor;
|
||||
private readonly ISaveProgressionTask _saveProgressionTask;
|
||||
private readonly ILogger<StatusPublicationPipeline> _logger;
|
||||
|
||||
#region Ctor
|
||||
public StatusPublicationPipeline(IRetrieveTweetsProcessor retrieveTweetsProcessor, IRetrieveTwitterUsersProcessor retrieveTwitterAccountsProcessor, IRetrieveFollowersProcessor retrieveFollowersProcessor, ISendTweetsToFollowersProcessor sendTweetsToFollowersProcessor, ISaveProgressionTask saveProgressionTask, ILogger<StatusPublicationPipeline> logger)
|
||||
public StatusPublicationPipeline(IRetrieveTweetsProcessor retrieveTweetsProcessor, IRetrieveTwitterUsersProcessor retrieveTwitterAccountsProcessor, IRetrieveFollowersProcessor retrieveFollowersProcessor, ISendTweetsToFollowersProcessor sendTweetsToFollowersProcessor, ILogger<StatusPublicationPipeline> logger)
|
||||
{
|
||||
_retrieveTweetsProcessor = retrieveTweetsProcessor;
|
||||
_retrieveFollowersProcessor = retrieveFollowersProcessor;
|
||||
_sendTweetsToFollowersProcessor = sendTweetsToFollowersProcessor;
|
||||
_saveProgressionTask = saveProgressionTask;
|
||||
_retrieveTwitterAccountsProcessor = retrieveTwitterAccountsProcessor;
|
||||
|
||||
_logger = logger;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace BirdsiteLive.Twitter
|
|||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, "Error retrieving timeline of {Username}; refreshing client", username);
|
||||
//await _twitterAuthenticationInitializer.RefreshClient(request);
|
||||
await _twitterAuthenticationInitializer.RefreshClient(request);
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -47,10 +47,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Moderation.Tes
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Common.Tests", "Tests\BirdsiteLive.Common.Tests\BirdsiteLive.Common.Tests.csproj", "{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BSLManager", "BSLManager\BSLManager.csproj", "{4A84D351-E91B-4E58-8E20-211F0F4991D7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSLManager.Tests", "Tests\BSLManager.Tests\BSLManager.Tests.csproj", "{D4457271-620E-465A-B08E-7FC63C99A2F6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.Twitter.Tests", "Tests\BirdsiteLive.Twitter.Tests\BirdsiteLive.Twitter.Tests.csproj", "{2DFA0BFD-88F5-4434-A6E3-C93B5750E88C}"
|
||||
EndProject
|
||||
Global
|
||||
|
@ -131,14 +127,6 @@ Global
|
|||
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2DFA0BFD-88F5-4434-A6E3-C93B5750E88C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2DFA0BFD-88F5-4434-A6E3-C93B5750E88C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2DFA0BFD-88F5-4434-A6E3-C93B5750E88C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -165,7 +153,6 @@ Global
|
|||
{4BE541AC-8A93-4FA3-98AC-956CC2D5B748} = {DA3C160C-4811-4E26-A5AD-42B81FAF2D7C}
|
||||
{0A311BF3-4FD9-4303-940A-A3778890561C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
{D4457271-620E-465A-B08E-7FC63C99A2F6} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
{2DFA0BFD-88F5-4434-A6E3-C93B5750E88C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<Version>1.0</Version>
|
||||
|
|
|
@ -11,6 +11,8 @@ using BirdsiteLive.ActivityPub;
|
|||
using BirdsiteLive.ActivityPub.Models;
|
||||
using BirdsiteLive.Common.Regexes;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BirdsiteLive.DAL.Models;
|
||||
using BirdsiteLive.Domain;
|
||||
using BirdsiteLive.Models;
|
||||
using BirdsiteLive.Tools;
|
||||
|
@ -30,17 +32,21 @@ namespace BirdsiteLive.Controllers
|
|||
private readonly IUserService _userService;
|
||||
private readonly IStatusService _statusService;
|
||||
private readonly InstanceSettings _instanceSettings;
|
||||
private readonly IFollowersDal _followersDal;
|
||||
private readonly ITwitterUserDal _twitterUserDal;
|
||||
private readonly IActivityPubService _activityPubService;
|
||||
private readonly ILogger<UsersController> _logger;
|
||||
|
||||
#region Ctor
|
||||
public UsersController(ICachedTwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ICachedTwitterTweetsService twitterTweetService, IActivityPubService activityPubService, ILogger<UsersController> logger)
|
||||
public UsersController(ICachedTwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ICachedTwitterTweetsService twitterTweetService, IFollowersDal followersDal, ITwitterUserDal twitterUserDal, IActivityPubService activityPubService, ILogger<UsersController> logger)
|
||||
{
|
||||
_twitterUserService = twitterUserService;
|
||||
_userService = userService;
|
||||
_statusService = statusService;
|
||||
_instanceSettings = instanceSettings;
|
||||
_twitterTweetService = twitterTweetService;
|
||||
_followersDal = followersDal;
|
||||
_twitterUserDal = twitterUserDal;
|
||||
_activityPubService = activityPubService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -121,6 +127,12 @@ namespace BirdsiteLive.Controllers
|
|||
if (isSaturated) return View("ApiSaturated");
|
||||
if (notFound) return View("UserNotFound");
|
||||
|
||||
Follower[] followers = new Follower[] { };
|
||||
|
||||
var userDal = await _twitterUserDal.GetTwitterUserAsync(user.Acct);
|
||||
if (userDal != null)
|
||||
followers = await _followersDal.GetFollowersAsync(userDal.Id);
|
||||
|
||||
var displayableUser = new DisplayTwitterUser
|
||||
{
|
||||
Name = user.Name,
|
||||
|
@ -129,6 +141,8 @@ namespace BirdsiteLive.Controllers
|
|||
Url = user.Url,
|
||||
ProfileImageUrl = user.ProfileImageUrl,
|
||||
Protected = user.Protected,
|
||||
FollowerCount = followers.Length,
|
||||
MostPopularServer = followers.GroupBy(x => x.Host).OrderByDescending(x => x.Count()).Select(x => x.Key).FirstOrDefault("N/A"),
|
||||
|
||||
InstanceHandle = $"@{user.Acct.ToLowerInvariant()}@{_instanceSettings.Domain}"
|
||||
};
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
public string Url { get; set; }
|
||||
public string ProfileImageUrl { get; set; }
|
||||
public bool Protected { get; set; }
|
||||
public int FollowerCount { get; set; }
|
||||
public string MostPopularServer { get; set; }
|
||||
|
||||
public string InstanceHandle { get; set; }
|
||||
}
|
||||
|
|
|
@ -12,18 +12,21 @@
|
|||
</p>
|
||||
|
||||
<form method="POST">
|
||||
@*<div class="form-group">
|
||||
<label for="exampleInputEmail1">Email address</label>
|
||||
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
|
||||
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
|
||||
</div>*@
|
||||
<div class="form-group">
|
||||
@*<label for="exampleInputPassword1">Password</label>*@
|
||||
<input type="text" class="form-control col-8 col-sm-8 col-md-6 col-lg-4 mx-auto" id="handle" name="handle" autocomplete="off" placeholder="Twitter Handle">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Show</button>
|
||||
</form>
|
||||
|
||||
<p style = "padding-top: 100px;">
|
||||
<br />
|
||||
bird.makeup is made with ❤️ by <a href="https://social.librem.one/@@vincent"> Vincent Cloutier</a> in 🇨🇦
|
||||
<br />
|
||||
<br />
|
||||
Many thanks to our top Patreon supporters: <br/>
|
||||
<a href="https://mstdn-social.com/@@fishcharlie">Charlie Fish</a>
|
||||
|
||||
</p>
|
||||
|
||||
@*@if (HtmlHelperExtensions.IsDebug())
|
||||
{
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
</div>
|
||||
</a>
|
||||
<br />
|
||||
<div>
|
||||
This account has @ViewData.Model.FollowerCount followers on the fediverse. The server with the most followers for this account is: @ViewData.Model.MostPopularServer
|
||||
</div>
|
||||
<br />
|
||||
|
||||
@if (ViewData.Model.Protected)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
public class DbInitializerPostgresDal : PostgresBase, IDbInitializerDal
|
||||
{
|
||||
private readonly PostgresTools _tools;
|
||||
private readonly Version _currentVersion = new Version(2, 5);
|
||||
private readonly Version _currentVersion = new Version(3, 0);
|
||||
private const string DbVersionType = "db-version";
|
||||
|
||||
#region Ctor
|
||||
|
@ -136,7 +136,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
new Tuple<Version, Version>(new Version(2,1), new Version(2,2)),
|
||||
new Tuple<Version, Version>(new Version(2,2), new Version(2,3)),
|
||||
new Tuple<Version, Version>(new Version(2,3), new Version(2,4)),
|
||||
new Tuple<Version, Version>(new Version(2,4), new Version(2,5))
|
||||
new Tuple<Version, Version>(new Version(2,4), new Version(2,5)),
|
||||
new Tuple<Version, Version>(new Version(2,5), new Version(3,0))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -179,6 +180,48 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
await _tools.ExecuteRequestAsync(alterTwitterUserId);
|
||||
|
||||
}
|
||||
else if (from == new Version(2, 5) && to == new Version(3, 0))
|
||||
{
|
||||
var dropFollowingSyncStatus = $@"ALTER TABLE {_settings.FollowersTableName} DROP COLUMN followingssyncstatus";
|
||||
await _tools.ExecuteRequestAsync(dropFollowingSyncStatus);
|
||||
|
||||
var dropLastTweet = $@"ALTER TABLE {_settings.TwitterUserTableName} DROP COLUMN lasttweetsynchronizedforallfollowersid";
|
||||
await _tools.ExecuteRequestAsync(dropLastTweet);
|
||||
|
||||
var addFediverseEquivalent = $@"ALTER TABLE {_settings.TwitterUserTableName} ADD fediverseaccount text";
|
||||
await _tools.ExecuteRequestAsync(addFediverseEquivalent);
|
||||
|
||||
var createWorkers = $@"CREATE TABLE {_settings.WorkersTableName}
|
||||
(
|
||||
id BIGINT PRIMARY KEY,
|
||||
rangeStart INTEGER,
|
||||
rangeEnd INTEGER,
|
||||
lastSeen TIMESTAMP (2) WITHOUT TIME ZONE,
|
||||
name text
|
||||
);";
|
||||
await _tools.ExecuteRequestAsync(createWorkers);
|
||||
|
||||
var createWorkerInstance = $@" INSERT INTO {_settings.WorkersTableName} (id,rangeStart,rangeEnd) VALUES(0,0, 100) ";
|
||||
await _tools.ExecuteRequestAsync(createWorkerInstance);
|
||||
|
||||
var createInstagram = $@"CREATE TABLE {_settings.InstagramUserTableName}
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
acct VARCHAR(20) UNIQUE,
|
||||
|
||||
data JSONB
|
||||
);";
|
||||
await _tools.ExecuteRequestAsync(createInstagram);
|
||||
|
||||
var createInstagramPost = $@"CREATE TABLE {_settings.CachedInstaPostsTableName}
|
||||
(
|
||||
id VARCHAR(30) PRIMARY KEY,
|
||||
acct VARCHAR(20) UNIQUE,
|
||||
|
||||
data JSONB
|
||||
);";
|
||||
await _tools.ExecuteRequestAsync(createInstagramPost);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
@ -207,7 +250,10 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
$@"DROP TABLE {_settings.DbVersionTableName};",
|
||||
$@"DROP TABLE {_settings.TwitterUserTableName};",
|
||||
$@"DROP TABLE {_settings.FollowersTableName};",
|
||||
$@"DROP TABLE {_settings.CachedTweetsTableName};"
|
||||
$@"DROP TABLE {_settings.CachedTweetsTableName};",
|
||||
$@"DROP TABLE {_settings.InstagramUserTableName};",
|
||||
$@"DROP TABLE {_settings.CachedInstaPostsTableName};",
|
||||
$@"DROP TABLE {_settings.WorkersTableName};"
|
||||
};
|
||||
|
||||
foreach (var r in dropsRequests)
|
||||
|
|
|
@ -21,12 +21,9 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
}
|
||||
#endregion
|
||||
|
||||
public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, int[] followings = null, Dictionary<int, long> followingSyncStatus = null)
|
||||
public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, int[] followings = null)
|
||||
{
|
||||
if(followings == null) followings = new int[0];
|
||||
if(followingSyncStatus == null) followingSyncStatus = new Dictionary<int, long>();
|
||||
|
||||
var serializedDic = JsonSerializer.Serialize(followingSyncStatus);
|
||||
|
||||
acct = acct.ToLowerInvariant();
|
||||
host = host.ToLowerInvariant();
|
||||
|
@ -34,8 +31,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
using (var dbConnection = Connection)
|
||||
{
|
||||
await dbConnection.ExecuteAsync(
|
||||
$"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxRoute,sharedInboxRoute,followings,followingsSyncStatus,actorId) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,CAST(@followingsSyncStatus as json),@actorId)",
|
||||
new { acct, host, inboxRoute, sharedInboxRoute, followings, followingsSyncStatus = serializedDic, actorId });
|
||||
$"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxRoute,sharedInboxRoute,followings,actorId) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,@actorId)",
|
||||
new { acct, host, inboxRoute, sharedInboxRoute, followings, actorId });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,13 +75,10 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
if (!await reader.ReadAsync())
|
||||
return null;
|
||||
|
||||
string syncStatusString = reader["followingsSyncStatus"] as string;
|
||||
var syncStatus = System.Text.Json.JsonSerializer.Deserialize<Dictionary<int, long>>(syncStatusString);
|
||||
return new Follower
|
||||
{
|
||||
Id = reader["id"] as int? ?? default,
|
||||
Followings = (reader["followings"] as int[] ?? new int[0]).ToList(),
|
||||
FollowingsSyncStatus = syncStatus,
|
||||
ActorId = reader["actorId"] as string,
|
||||
Acct = reader["acct"] as string,
|
||||
Host = reader["host"] as string,
|
||||
|
@ -111,13 +105,10 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
var followers = new List<Follower>();
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
string syncStatusString = reader["followingsSyncStatus"] as string;
|
||||
var syncStatus = System.Text.Json.JsonSerializer.Deserialize<Dictionary<int, long>>(syncStatusString);
|
||||
followers.Add(new Follower
|
||||
{
|
||||
Id = reader["id"] as int? ?? default,
|
||||
Followings = (reader["followings"] as int[] ?? new int[0]).ToList(),
|
||||
FollowingsSyncStatus = syncStatus,
|
||||
ActorId = reader["actorId"] as string,
|
||||
Acct = reader["acct"] as string,
|
||||
Host = reader["host"] as string,
|
||||
|
@ -147,14 +138,12 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
if (follower == default) throw new ArgumentException("follower");
|
||||
if (follower.Id == default) throw new ArgumentException("id");
|
||||
|
||||
var serializedDic = System.Text.Json.JsonSerializer.Serialize(follower.FollowingsSyncStatus);
|
||||
var query = $"UPDATE {_settings.FollowersTableName} SET followings = $1, followingsSyncStatus = CAST($2 as json), postingErrorCount = $3 WHERE id = $4";
|
||||
var query = $"UPDATE {_settings.FollowersTableName} SET followings = $1, postingErrorCount = $2 WHERE id = $3";
|
||||
await using var connection = DataSource.CreateConnection();
|
||||
await connection.OpenAsync();
|
||||
await using var command = new NpgsqlCommand(query, connection) {
|
||||
Parameters = {
|
||||
new() { Value = follower.Followings},
|
||||
new() { Value = serializedDic},
|
||||
new() { Value = follower.PostingErrorCount},
|
||||
new() { Value = follower.Id}
|
||||
}
|
||||
|
@ -204,7 +193,6 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
ActorId = follower.ActorId,
|
||||
SharedInboxRoute = follower.SharedInboxRoute,
|
||||
Followings = follower.Followings.ToList(),
|
||||
FollowingsSyncStatus = JsonSerializer.Deserialize<Dictionary<int,long>>(follower.FollowingsSyncStatus),
|
||||
PostingErrorCount = follower.PostingErrorCount
|
||||
};
|
||||
}
|
||||
|
@ -212,10 +200,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
|
||||
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; }
|
||||
public string InboxRoute { get; set; }
|
||||
|
|
|
@ -27,8 +27,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
using (var dbConnection = Connection)
|
||||
{
|
||||
await dbConnection.ExecuteAsync(
|
||||
$"INSERT INTO {_settings.TwitterUserTableName} (acct,lastTweetPostedId,lastTweetSynchronizedForAllFollowersId) VALUES(@acct,@lastTweetPostedId,@lastTweetSynchronizedForAllFollowersId)",
|
||||
new { acct, lastTweetPostedId, lastTweetSynchronizedForAllFollowersId = lastTweetPostedId });
|
||||
$"INSERT INTO {_settings.TwitterUserTableName} (acct,lastTweetPostedId) VALUES(@acct,@lastTweetPostedId)",
|
||||
new { acct, lastTweetPostedId });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
Acct = reader["acct"] as string,
|
||||
TwitterUserId = reader["twitterUserId"] as long? ?? default,
|
||||
LastTweetPostedId = reader["lastTweetPostedId"] as long? ?? default,
|
||||
LastTweetSynchronizedForAllFollowersId = reader["lastTweetSynchronizedForAllFollowersId"] as long? ?? default,
|
||||
LastSync = reader["lastSync"] as DateTime? ?? default,
|
||||
FetchingErrorCount = reader["fetchingErrorCount"] as int? ?? default,
|
||||
};
|
||||
|
@ -79,7 +78,6 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
Acct = reader["acct"] as string,
|
||||
TwitterUserId = reader["twitterUserId"] as long? ?? default,
|
||||
LastTweetPostedId = reader["lastTweetPostedId"] as long? ?? default,
|
||||
LastTweetSynchronizedForAllFollowersId = reader["lastTweetSynchronizedForAllFollowersId"] as long? ?? default,
|
||||
LastSync = reader["lastSync"] as DateTime? ?? default,
|
||||
FetchingErrorCount = reader["fetchingErrorCount"] as int? ?? default,
|
||||
};
|
||||
|
@ -143,7 +141,6 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
Acct = reader["acct"] as string,
|
||||
TwitterUserId = reader["twitterUserId"] as long? ?? default,
|
||||
LastTweetPostedId = reader["lastTweetPostedId"] as long? ?? default,
|
||||
LastTweetSynchronizedForAllFollowersId = reader["lastTweetSynchronizedForAllFollowersId"] as long? ?? default,
|
||||
LastSync = reader["lastSync"] as DateTime? ?? default,
|
||||
FetchingErrorCount = reader["fetchingErrorCount"] as int? ?? default,
|
||||
}
|
||||
|
@ -189,21 +186,19 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
public async Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId, int fetchingErrorCount, DateTime lastSync)
|
||||
public async Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, int fetchingErrorCount, DateTime lastSync)
|
||||
{
|
||||
if(id == default) throw new ArgumentException("id");
|
||||
if(lastTweetPostedId == default) throw new ArgumentException("lastTweetPostedId");
|
||||
if(lastTweetSynchronizedForAllFollowersId == default) throw new ArgumentException("lastTweetSynchronizedForAllFollowersId");
|
||||
if(lastSync == default) throw new ArgumentException("lastSync");
|
||||
|
||||
var query = $"UPDATE {_settings.TwitterUserTableName} SET lastTweetPostedId = $1, lastTweetSynchronizedForAllFollowersId = $2, fetchingErrorCount = $3, lastSync = $4 WHERE id = $5";
|
||||
var query = $"UPDATE {_settings.TwitterUserTableName} SET lastTweetPostedId = $1, fetchingErrorCount = $2, lastSync = $3 WHERE id = $4";
|
||||
|
||||
await using var connection = DataSource.CreateConnection();
|
||||
await connection.OpenAsync();
|
||||
await using var command = new NpgsqlCommand(query, connection) {
|
||||
Parameters = {
|
||||
new() { Value = lastTweetPostedId},
|
||||
new() { Value = lastTweetSynchronizedForAllFollowersId},
|
||||
new() { Value = fetchingErrorCount},
|
||||
new() { Value = lastSync.ToUniversalTime()},
|
||||
new() { Value = id},
|
||||
|
@ -215,7 +210,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
|||
|
||||
public async Task UpdateTwitterUserAsync(SyncTwitterUser user)
|
||||
{
|
||||
await UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.LastTweetSynchronizedForAllFollowersId, user.FetchingErrorCount, user.LastSync);
|
||||
await UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.FetchingErrorCount, user.LastSync);
|
||||
}
|
||||
|
||||
public async Task DeleteTwitterUserAsync(string acct)
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
public string DbVersionTableName { get; set; } = "db_version";
|
||||
public string TwitterUserTableName { get; set; } = "twitter_users";
|
||||
public string InstagramUserTableName { get; set; } = "instagram_users";
|
||||
public string WorkersTableName { get; set; } = "workers";
|
||||
public string CachedInstaPostsTableName { get; set; } = "cached_insta_posts";
|
||||
public string FollowersTableName { get; set; } = "followers";
|
||||
public string CachedTweetsTableName { get; set; } = "cached_tweets";
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ namespace BirdsiteLive.DAL.Contracts
|
|||
public interface IFollowersDal
|
||||
{
|
||||
Task<Follower> GetFollowerAsync(string acct, string host);
|
||||
Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, int[] followings = null,
|
||||
Dictionary<int, long> followingSyncStatus = null);
|
||||
Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, int[] followings = null);
|
||||
Task<Follower[]> GetFollowersAsync(int followedUserId);
|
||||
Task<Follower[]> GetAllFollowersAsync();
|
||||
Task UpdateFollowerAsync(Follower follower);
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace BirdsiteLive.DAL.Contracts
|
|||
Task<SyncTwitterUser[]> GetAllTwitterUsersWithFollowersAsync(int maxNumber, int nStart, int nEnd, int m);
|
||||
Task<SyncTwitterUser[]> GetAllTwitterUsersAsync(int maxNumber);
|
||||
Task<SyncTwitterUser[]> GetAllTwitterUsersAsync();
|
||||
Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId, int fetchingErrorCount, DateTime lastSync);
|
||||
Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, int fetchingErrorCount, DateTime lastSync);
|
||||
Task UpdateTwitterUserIdAsync(string username, long twitterUserId);
|
||||
Task UpdateTwitterUserAsync(SyncTwitterUser user);
|
||||
Task DeleteTwitterUserAsync(string acct);
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace BirdsiteLive.DAL.Models
|
|||
public int Id { get; set; }
|
||||
|
||||
public List<int> Followings { get; set; }
|
||||
public Dictionary<int, long> FollowingsSyncStatus { get; set; }
|
||||
|
||||
public string ActorId { get; set; }
|
||||
public string Acct { get; set; }
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace BirdsiteLive.DAL.Models
|
|||
public string Acct { get; set; }
|
||||
|
||||
public long LastTweetPostedId { get; set; }
|
||||
public long LastTweetSynchronizedForAllFollowersId { get; set; }
|
||||
|
||||
public DateTime LastSync { get; set; }
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\BSLManager\BSLManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,307 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BirdsiteLive.DAL.Models;
|
||||
using BSLManager.Domain;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace BSLManager.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class FollowersListStateTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void FilterBy()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers);
|
||||
|
||||
state.FilterBy("test");
|
||||
|
||||
#region Validate
|
||||
Assert.AreEqual(2, state.GetDisplayableList().Count);
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FilterBy_GetElement()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers);
|
||||
|
||||
state.FilterBy("test");
|
||||
var el = state.GetElementAt(1);
|
||||
|
||||
#region Validate
|
||||
Assert.AreEqual(followers[1].Id, el.Id);
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetElement()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers);
|
||||
|
||||
var el = state.GetElementAt(2);
|
||||
|
||||
#region Validate
|
||||
Assert.AreEqual(followers[2].Id, el.Id);
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FilterBy_RemoveAt()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers.ToList());
|
||||
|
||||
state.FilterBy("test");
|
||||
state.RemoveAt(1);
|
||||
|
||||
var list = state.GetDisplayableList();
|
||||
|
||||
#region Validate
|
||||
Assert.AreEqual(1, list.Count);
|
||||
Assert.IsTrue(list[0].Contains("@test@host1"));
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAt()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers.ToList());
|
||||
|
||||
state.RemoveAt(1);
|
||||
|
||||
var list = state.GetDisplayableList();
|
||||
|
||||
#region Validate
|
||||
Assert.AreEqual(3, list.Count);
|
||||
Assert.IsTrue(list[0].Contains("@test@host1"));
|
||||
Assert.IsFalse(list[1].Contains("@test@host2"));
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FilterBy_ResetFilter()
|
||||
{
|
||||
#region Stub
|
||||
var followers = new List<Follower>
|
||||
{
|
||||
new Follower
|
||||
{
|
||||
Id = 0,
|
||||
Acct = "test",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 1,
|
||||
Acct = "test",
|
||||
Host = "host2",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Acct = "user1",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Acct = "user2",
|
||||
Host = "host1",
|
||||
Followings = new List<int>()
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
var state = new FollowersListState();
|
||||
state.Load(followers.ToList());
|
||||
|
||||
#region Validate
|
||||
state.FilterBy("data");
|
||||
var list = state.GetDisplayableList();
|
||||
Assert.AreEqual(0, list.Count);
|
||||
|
||||
state.FilterBy(string.Empty);
|
||||
list = state.GetDisplayableList();
|
||||
Assert.AreEqual(4, list.Count);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -19,6 +19,9 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers.Base
|
|||
CachedTweetsTableName = "CachedTweetsTableName" + RandomGenerator.GetString(4),
|
||||
FollowersTableName = "FollowersTableName" + RandomGenerator.GetString(4),
|
||||
TwitterUserTableName = "TwitterUserTableName" + RandomGenerator.GetString(4),
|
||||
InstagramUserTableName = "InstagramUserTableName" + RandomGenerator.GetString(4),
|
||||
CachedInstaPostsTableName = "CachedInstaPosts" + RandomGenerator.GetString(4),
|
||||
WorkersTableName = "workers" + RandomGenerator.GetString(4),
|
||||
};
|
||||
_tools = new PostgresTools(_settings);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
|
@ -57,9 +57,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
Assert.AreEqual(0, result.PostingErrorCount);
|
||||
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]
|
||||
|
@ -72,7 +69,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, null, null);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, null);
|
||||
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
|
@ -83,7 +80,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
Assert.AreEqual(inboxRoute, result.InboxRoute);
|
||||
Assert.AreEqual(sharedInboxRoute, result.SharedInboxRoute);
|
||||
Assert.AreEqual(0, result.Followings.Count);
|
||||
Assert.AreEqual(0, result.FollowingsSyncStatus.Count);
|
||||
Assert.AreEqual(0, result.PostingErrorCount);
|
||||
}
|
||||
|
||||
|
@ -112,7 +108,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
|
@ -124,9 +120,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
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);
|
||||
Assert.AreEqual(0, result.PostingErrorCount);
|
||||
}
|
||||
|
||||
|
@ -143,7 +136,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var inboxRoute = "/myhandle1/inbox";
|
||||
var sharedInboxRoute = "/inbox";
|
||||
var actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle2";
|
||||
|
@ -152,7 +145,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle2/inbox";
|
||||
sharedInboxRoute = "/inbox2";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle3";
|
||||
|
@ -161,7 +154,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle3/inbox";
|
||||
sharedInboxRoute = "/inbox3";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
var result = await dal.GetFollowersAsync(2);
|
||||
Assert.AreEqual(2, result.Length);
|
||||
|
@ -186,7 +179,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var inboxRoute = "/myhandle1/inbox";
|
||||
var sharedInboxRoute = "/inbox";
|
||||
var actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle2";
|
||||
|
@ -195,7 +188,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle2/inbox";
|
||||
sharedInboxRoute = "/inbox2";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle3";
|
||||
|
@ -204,7 +197,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle3/inbox";
|
||||
sharedInboxRoute = "/inbox3";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
var result = await dal.GetAllFollowersAsync();
|
||||
Assert.AreEqual(3, result.Length);
|
||||
|
@ -226,7 +219,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var inboxRoute = "/myhandle1/inbox";
|
||||
var sharedInboxRoute = "/inbox";
|
||||
var actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle2";
|
||||
|
@ -235,7 +228,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle2/inbox";
|
||||
sharedInboxRoute = "/inbox2";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 3
|
||||
acct = "myhandle3";
|
||||
|
@ -244,7 +237,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle3/inbox";
|
||||
sharedInboxRoute = "/inbox3";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
result = await dal.GetFollowersCountAsync();
|
||||
Assert.AreEqual(3, result);
|
||||
|
@ -266,7 +259,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var inboxRoute = "/myhandle1/inbox";
|
||||
var sharedInboxRoute = "/inbox";
|
||||
var actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
//User 2
|
||||
acct = "myhandle2";
|
||||
|
@ -275,7 +268,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle2/inbox";
|
||||
sharedInboxRoute = "/inbox2";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
var follower = await dal.GetFollowerAsync(acct, host);
|
||||
follower.PostingErrorCount = 1;
|
||||
|
@ -288,7 +281,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
inboxRoute = "/myhandle3/inbox";
|
||||
sharedInboxRoute = "/inbox3";
|
||||
actorId = $"https://{host}/{acct}";
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
|
||||
follower = await dal.GetFollowerAsync(acct, host);
|
||||
follower.PostingErrorCount = 50;
|
||||
|
@ -315,7 +308,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
var updatedFollowing = new List<int> { 12, 19, 23, 24 };
|
||||
|
@ -326,7 +319,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
{24, 173L}
|
||||
};
|
||||
result.Followings = updatedFollowing.ToList();
|
||||
result.FollowingsSyncStatus = updatedFollowingSync;
|
||||
result.PostingErrorCount = 10;
|
||||
|
||||
await dal.UpdateFollowerAsync(result);
|
||||
|
@ -334,9 +326,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(updatedFollowing.Count, result.Followings.Count);
|
||||
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);
|
||||
Assert.AreEqual(10, result.PostingErrorCount);
|
||||
}
|
||||
|
||||
|
@ -357,7 +346,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
var updatedFollowing = new List<int> { 12, 19, 23, 24 };
|
||||
|
@ -368,7 +357,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
{24, 173L}
|
||||
};
|
||||
result.Followings = updatedFollowing.ToList();
|
||||
result.FollowingsSyncStatus = updatedFollowingSync;
|
||||
result.PostingErrorCount = 32768;
|
||||
|
||||
await dal.UpdateFollowerAsync(result);
|
||||
|
@ -376,9 +364,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(updatedFollowing.Count, result.Followings.Count);
|
||||
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);
|
||||
Assert.AreEqual(32768, result.PostingErrorCount);
|
||||
}
|
||||
|
||||
|
@ -399,7 +384,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
|
||||
var updatedFollowing = new[] { 12, 19 };
|
||||
|
@ -409,7 +394,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
{19, 171L}
|
||||
};
|
||||
result.Followings = updatedFollowing.ToList();
|
||||
result.FollowingsSyncStatus = updatedFollowingSync;
|
||||
result.PostingErrorCount = 5;
|
||||
|
||||
await dal.UpdateFollowerAsync(result);
|
||||
|
@ -417,9 +401,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(updatedFollowing.Length, result.Followings.Count);
|
||||
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);
|
||||
Assert.AreEqual(5, result.PostingErrorCount);
|
||||
}
|
||||
|
||||
|
@ -440,7 +421,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
Assert.AreEqual(0, result.PostingErrorCount);
|
||||
|
||||
|
@ -495,7 +476,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
|
@ -522,7 +503,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var actorId = $"https://{host}/{acct}";
|
||||
|
||||
var dal = new FollowersPostgresDal(_settings);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
|
||||
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following);
|
||||
var result = await dal.GetFollowerAsync(acct, host);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(acct, result.Acct);
|
||||
Assert.AreEqual(lastTweetId, result.LastTweetPostedId);
|
||||
Assert.AreEqual(lastTweetId, result.LastTweetSynchronizedForAllFollowersId);
|
||||
Assert.AreEqual(0, result.FetchingErrorCount);
|
||||
Assert.IsTrue(result.Id > 0);
|
||||
}
|
||||
|
@ -67,7 +66,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(acct, resultById.Acct);
|
||||
Assert.AreEqual(lastTweetId, resultById.LastTweetPostedId);
|
||||
Assert.AreEqual(lastTweetId, resultById.LastTweetSynchronizedForAllFollowersId);
|
||||
Assert.AreEqual(result.Id, resultById.Id);
|
||||
}
|
||||
|
||||
|
@ -87,13 +85,12 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var updatedLastSyncId = 1550L;
|
||||
var now = DateTime.Now;
|
||||
var errors = 15;
|
||||
await dal.UpdateTwitterUserAsync(result.Id, updatedLastTweetId, updatedLastSyncId, errors, now);
|
||||
await dal.UpdateTwitterUserAsync(result.Id, updatedLastTweetId, errors, now);
|
||||
|
||||
result = await dal.GetTwitterUserAsync(acct);
|
||||
|
||||
Assert.AreEqual(acct, result.Acct);
|
||||
Assert.AreEqual(updatedLastTweetId, result.LastTweetPostedId);
|
||||
Assert.AreEqual(updatedLastSyncId, result.LastTweetSynchronizedForAllFollowersId);
|
||||
Assert.AreEqual(errors, result.FetchingErrorCount);
|
||||
Assert.IsTrue(Math.Abs((now.ToUniversalTime() - result.LastSync).Milliseconds) < 100);
|
||||
}
|
||||
|
@ -116,7 +113,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var errors = 15;
|
||||
|
||||
result.LastTweetPostedId = updatedLastTweetId;
|
||||
result.LastTweetSynchronizedForAllFollowersId = updatedLastSyncId;
|
||||
result.FetchingErrorCount = errors;
|
||||
result.LastSync = now;
|
||||
await dal.UpdateTwitterUserAsync(result);
|
||||
|
@ -125,7 +121,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(acct, result.Acct);
|
||||
Assert.AreEqual(updatedLastTweetId, result.LastTweetPostedId);
|
||||
Assert.AreEqual(updatedLastSyncId, result.LastTweetSynchronizedForAllFollowersId);
|
||||
Assert.AreEqual(errors, result.FetchingErrorCount);
|
||||
Assert.IsTrue(Math.Abs((now.ToUniversalTime() - result.LastSync).Milliseconds) < 100);
|
||||
}
|
||||
|
@ -148,7 +143,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
var errors = 32768;
|
||||
|
||||
result.LastTweetPostedId = updatedLastTweetId;
|
||||
result.LastTweetSynchronizedForAllFollowersId = updatedLastSyncId;
|
||||
result.FetchingErrorCount = errors;
|
||||
result.LastSync = now;
|
||||
await dal.UpdateTwitterUserAsync(result);
|
||||
|
@ -157,7 +151,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
|
||||
Assert.AreEqual(acct, result.Acct);
|
||||
Assert.AreEqual(updatedLastTweetId, result.LastTweetPostedId);
|
||||
Assert.AreEqual(updatedLastSyncId, result.LastTweetSynchronizedForAllFollowersId);
|
||||
Assert.AreEqual(errors, result.FetchingErrorCount);
|
||||
Assert.IsTrue(Math.Abs((now.ToUniversalTime() - result.LastSync).Milliseconds) < 100);
|
||||
}
|
||||
|
@ -167,7 +160,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
public async Task Update_NoId()
|
||||
{
|
||||
var dal = new TwitterUserPostgresDal(_settings);
|
||||
await dal.UpdateTwitterUserAsync(default, default, default, default, DateTime.UtcNow);
|
||||
await dal.UpdateTwitterUserAsync(default, default, default, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -175,23 +168,16 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
public async Task Update_NoLastTweetPostedId()
|
||||
{
|
||||
var dal = new TwitterUserPostgresDal(_settings);
|
||||
await dal.UpdateTwitterUserAsync(12, default, default, default, DateTime.UtcNow);
|
||||
await dal.UpdateTwitterUserAsync(12, default, default, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public async Task Update_NoLastTweetSynchronizedForAllFollowersId()
|
||||
{
|
||||
var dal = new TwitterUserPostgresDal(_settings);
|
||||
await dal.UpdateTwitterUserAsync(12, 9556, default, default, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public async Task Update_NoLastSync()
|
||||
{
|
||||
var dal = new TwitterUserPostgresDal(_settings);
|
||||
await dal.UpdateTwitterUserAsync(12, 9556, 65, default, default);
|
||||
await dal.UpdateTwitterUserAsync(12, 9556, 65, default);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -261,7 +247,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
Assert.IsFalse(result[0].Id == default);
|
||||
Assert.IsFalse(result[0].Acct == default);
|
||||
Assert.IsFalse(result[0].LastTweetPostedId == default);
|
||||
Assert.IsFalse(result[0].LastTweetSynchronizedForAllFollowersId == default);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -318,7 +303,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
{
|
||||
var user = allUsers[i];
|
||||
var date = i % 2 == 0 ? oldest : newest;
|
||||
await dal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, user.LastTweetSynchronizedForAllFollowersId, 0, date);
|
||||
await dal.UpdateTwitterUserAsync(user.Id, user.LastTweetPostedId, 0, date);
|
||||
}
|
||||
|
||||
var result = await dal.GetAllTwitterUsersAsync(10);
|
||||
|
@ -326,7 +311,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
Assert.IsFalse(result[0].Id == default);
|
||||
Assert.IsFalse(result[0].Acct == default);
|
||||
Assert.IsFalse(result[0].LastTweetPostedId == default);
|
||||
Assert.IsFalse(result[0].LastTweetSynchronizedForAllFollowersId == default);
|
||||
|
||||
foreach (var acc in result)
|
||||
Assert.IsTrue(Math.Abs((acc.LastSync - oldest.ToUniversalTime()).TotalMilliseconds) < 1000);
|
||||
|
@ -349,7 +333,6 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
Assert.IsFalse(result[0].Id == default);
|
||||
Assert.IsFalse(result[0].Acct == default);
|
||||
Assert.IsFalse(result[0].LastTweetPostedId == default);
|
||||
Assert.IsFalse(result[0].LastTweetSynchronizedForAllFollowersId == default);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -382,7 +365,7 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
|||
if (i == 0 || i == 2 || i == 3)
|
||||
{
|
||||
var t = await dal.GetTwitterUserAsync(acct);
|
||||
await dal.UpdateTwitterUserAsync(t.Id ,1L,2L, 50+i*2, DateTime.Now);
|
||||
await dal.UpdateTwitterUserAsync(t.Id ,1L, 50+i*2, DateTime.Now);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -30,7 +30,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
SharedInboxRoute = followerInbox,
|
||||
InboxRoute = inbox,
|
||||
Followings = new List<int>(),
|
||||
FollowingsSyncStatus = new Dictionary<int, long>()
|
||||
};
|
||||
|
||||
var twitterUser = new SyncTwitterUser
|
||||
|
@ -38,7 +37,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Id = 2,
|
||||
Acct = twitterName,
|
||||
LastTweetPostedId = -1,
|
||||
LastTweetSynchronizedForAllFollowersId = -1
|
||||
};
|
||||
#endregion
|
||||
|
||||
|
@ -56,14 +54,12 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
It.Is<string>(y => y == followerInbox),
|
||||
It.Is<string>(y => y == inbox),
|
||||
It.Is<string>(y => y == actorId),
|
||||
null,
|
||||
null))
|
||||
null ))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
followersDalMock
|
||||
.Setup(x => x.UpdateFollowerAsync(
|
||||
It.Is<Follower>(y => y.Followings.Contains(twitterUser.Id)
|
||||
&& y.FollowingsSyncStatus[twitterUser.Id] == -1)
|
||||
It.Is<Follower>(y => y.Followings.Contains(twitterUser.Id))
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
|
@ -108,7 +104,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
SharedInboxRoute = followerInbox,
|
||||
InboxRoute = inbox,
|
||||
Followings = new List<int>(),
|
||||
FollowingsSyncStatus = new Dictionary<int, long>()
|
||||
};
|
||||
|
||||
var twitterUser = new SyncTwitterUser
|
||||
|
@ -116,7 +111,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Id = 2,
|
||||
Acct = twitterName,
|
||||
LastTweetPostedId = -1,
|
||||
LastTweetSynchronizedForAllFollowersId = -1
|
||||
};
|
||||
#endregion
|
||||
|
||||
|
@ -128,8 +122,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
|
||||
followersDalMock
|
||||
.Setup(x => x.UpdateFollowerAsync(
|
||||
It.Is<Follower>(y => y.Followings.Contains(twitterUser.Id)
|
||||
&& y.FollowingsSyncStatus[twitterUser.Id] == -1)
|
||||
It.Is<Follower>(y => y.Followings.Contains(twitterUser.Id) )
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Acct = username,
|
||||
Host = domain,
|
||||
Followings = new List<int>(),
|
||||
FollowingsSyncStatus = new Dictionary<int, long>()
|
||||
};
|
||||
#endregion
|
||||
|
||||
|
@ -91,7 +90,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Acct = username,
|
||||
Host = domain,
|
||||
Followings = new List<int> { 2, 3 },
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { 2, 460 }, { 3, 563} }
|
||||
};
|
||||
|
||||
var twitterUser = new SyncTwitterUser
|
||||
|
@ -99,7 +97,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Id = 2,
|
||||
Acct = twitterName,
|
||||
LastTweetPostedId = 460,
|
||||
LastTweetSynchronizedForAllFollowersId = 460
|
||||
};
|
||||
|
||||
var followerList = new List<Follower>
|
||||
|
@ -117,8 +114,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
|
||||
followersDalMock
|
||||
.Setup(x => x.UpdateFollowerAsync(
|
||||
It.Is<Follower>(y => !y.Followings.Contains(twitterUser.Id)
|
||||
&& !y.FollowingsSyncStatus.ContainsKey(twitterUser.Id))
|
||||
It.Is<Follower>(y => !y.Followings.Contains(twitterUser.Id) )
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
|
@ -155,7 +151,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Acct = username,
|
||||
Host = domain,
|
||||
Followings = new List<int> { 2 },
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { 2, 460 } }
|
||||
};
|
||||
|
||||
var twitterUser = new SyncTwitterUser
|
||||
|
@ -163,7 +158,6 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
|
|||
Id = 2,
|
||||
Acct = twitterName,
|
||||
LastTweetPostedId = 460,
|
||||
LastTweetSynchronizedForAllFollowersId = 460
|
||||
};
|
||||
|
||||
var followerList = new List<Follower>();
|
||||
|
|
|
@ -27,7 +27,6 @@ namespace BirdsiteLive.Moderation.Tests.Actions
|
|||
{
|
||||
Id = 48,
|
||||
Followings = new List<int>{ 24 },
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { 24, 1024 } }
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
@ -84,7 +83,6 @@ namespace BirdsiteLive.Moderation.Tests.Actions
|
|||
{
|
||||
Id = 48,
|
||||
Followings = new List<int>{ 24, 36 },
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { 24, 1024 }, { 36, 24 } }
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
@ -100,7 +98,6 @@ namespace BirdsiteLive.Moderation.Tests.Actions
|
|||
.Setup(x => x.UpdateFollowerAsync(
|
||||
It.Is<Follower>(y => y.Id == 48
|
||||
&& y.Followings.Count == 1
|
||||
&& y.FollowingsSyncStatus.Count == 1
|
||||
)))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,227 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.DAL.Contracts;
|
||||
using BirdsiteLive.DAL.Models;
|
||||
using BirdsiteLive.Pipeline.Models;
|
||||
using BirdsiteLive.Pipeline.Processors.SubTasks;
|
||||
using BirdsiteLive.Twitter.Models;
|
||||
using Castle.DynamicProxy.Contributors;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace BirdsiteLive.Pipeline.Tests.Processors
|
||||
{
|
||||
[TestClass]
|
||||
public class SaveProgressionProcessorTests
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task ProcessAsync_Test()
|
||||
{
|
||||
#region Stubs
|
||||
var user = new SyncTwitterUser
|
||||
{
|
||||
Id = 1
|
||||
};
|
||||
var tweet1 = new ExtractedTweet
|
||||
{
|
||||
Id = 36
|
||||
};
|
||||
var tweet2 = new ExtractedTweet
|
||||
{
|
||||
Id = 37
|
||||
};
|
||||
var follower1 = new Follower
|
||||
{
|
||||
FollowingsSyncStatus = new Dictionary<int, long>
|
||||
{
|
||||
{1, 37}
|
||||
}
|
||||
};
|
||||
|
||||
var usersWithTweets = new UserWithDataToSync
|
||||
{
|
||||
Tweets = new []
|
||||
{
|
||||
tweet1,
|
||||
tweet2
|
||||
},
|
||||
Followers = new []
|
||||
{
|
||||
follower1
|
||||
},
|
||||
User = user
|
||||
};
|
||||
|
||||
var loggerMock = new Mock<ILogger<SaveProgressionTask>>();
|
||||
#endregion
|
||||
|
||||
#region Mocks
|
||||
var twitterUserDalMock = new Mock<ITwitterUserDal>(MockBehavior.Strict);
|
||||
twitterUserDalMock
|
||||
.Setup(x => x.UpdateTwitterUserAsync(
|
||||
It.Is<int>(y => y == user.Id),
|
||||
It.Is<long>(y => y == tweet2.Id),
|
||||
It.Is<long>(y => y == tweet2.Id),
|
||||
It.Is<int>(y => y == 0),
|
||||
It.IsAny<DateTime>()
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
#endregion
|
||||
|
||||
var processor = new SaveProgressionTask(twitterUserDalMock.Object, loggerMock.Object);
|
||||
await processor.ProcessAsync(usersWithTweets, CancellationToken.None);
|
||||
|
||||
#region Validations
|
||||
twitterUserDalMock.VerifyAll();
|
||||
loggerMock.VerifyAll();
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ProcessAsync_PartiallySynchronized_Test()
|
||||
{
|
||||
#region Stubs
|
||||
var user = new SyncTwitterUser
|
||||
{
|
||||
Id = 1
|
||||
};
|
||||
var tweet1 = new ExtractedTweet
|
||||
{
|
||||
Id = 36
|
||||
};
|
||||
var tweet2 = new ExtractedTweet
|
||||
{
|
||||
Id = 37
|
||||
};
|
||||
var tweet3 = new ExtractedTweet
|
||||
{
|
||||
Id = 38
|
||||
};
|
||||
var follower1 = new Follower
|
||||
{
|
||||
FollowingsSyncStatus = new Dictionary<int, long>
|
||||
{
|
||||
{1, 37}
|
||||
}
|
||||
};
|
||||
|
||||
var usersWithTweets = new UserWithDataToSync
|
||||
{
|
||||
Tweets = new[]
|
||||
{
|
||||
tweet1,
|
||||
tweet2,
|
||||
tweet3
|
||||
},
|
||||
Followers = new[]
|
||||
{
|
||||
follower1
|
||||
},
|
||||
User = user
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Mocks
|
||||
var twitterUserDalMock = new Mock<ITwitterUserDal>(MockBehavior.Strict);
|
||||
twitterUserDalMock
|
||||
.Setup(x => x.UpdateTwitterUserAsync(
|
||||
It.Is<int>(y => y == user.Id),
|
||||
It.Is<long>(y => y == tweet3.Id),
|
||||
It.Is<long>(y => y == tweet2.Id),
|
||||
It.Is<int>(y => y == 0),
|
||||
It.IsAny<DateTime>()
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SaveProgressionTask>>();
|
||||
#endregion
|
||||
|
||||
var processor = new SaveProgressionTask(twitterUserDalMock.Object, loggerMock.Object);
|
||||
await processor.ProcessAsync(usersWithTweets, CancellationToken.None);
|
||||
|
||||
#region Validations
|
||||
twitterUserDalMock.VerifyAll();
|
||||
loggerMock.VerifyAll();
|
||||
#endregion
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ProcessAsync_PartiallySynchronized_MultiUsers_Test()
|
||||
{
|
||||
#region Stubs
|
||||
var user = new SyncTwitterUser
|
||||
{
|
||||
Id = 1
|
||||
};
|
||||
var tweet1 = new ExtractedTweet
|
||||
{
|
||||
Id = 36
|
||||
};
|
||||
var tweet2 = new ExtractedTweet
|
||||
{
|
||||
Id = 37
|
||||
};
|
||||
var tweet3 = new ExtractedTweet
|
||||
{
|
||||
Id = 38
|
||||
};
|
||||
var follower1 = new Follower
|
||||
{
|
||||
FollowingsSyncStatus = new Dictionary<int, long>
|
||||
{
|
||||
{1, 37}
|
||||
}
|
||||
};
|
||||
var follower2 = new Follower
|
||||
{
|
||||
FollowingsSyncStatus = new Dictionary<int, long>
|
||||
{
|
||||
{1, 38}
|
||||
}
|
||||
};
|
||||
|
||||
var usersWithTweets = new UserWithDataToSync
|
||||
{
|
||||
Tweets = new[]
|
||||
{
|
||||
tweet1,
|
||||
tweet2,
|
||||
tweet3
|
||||
},
|
||||
Followers = new[]
|
||||
{
|
||||
follower1,
|
||||
follower2
|
||||
},
|
||||
User = user
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Mocks
|
||||
var twitterUserDalMock = new Mock<ITwitterUserDal>(MockBehavior.Strict);
|
||||
twitterUserDalMock
|
||||
.Setup(x => x.UpdateTwitterUserAsync(
|
||||
It.Is<int>(y => y == user.Id),
|
||||
It.Is<long>(y => y == tweet3.Id),
|
||||
It.Is<long>(y => y == tweet2.Id),
|
||||
It.Is<int>(y => y == 0),
|
||||
It.IsAny<DateTime>()
|
||||
))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SaveProgressionTask>>();
|
||||
#endregion
|
||||
|
||||
var processor = new SaveProgressionTask(twitterUserDalMock.Object, loggerMock.Object);
|
||||
await processor.ProcessAsync(usersWithTweets, CancellationToken.None);
|
||||
|
||||
#region Validations
|
||||
twitterUserDalMock.VerifyAll();
|
||||
loggerMock.VerifyAll();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,7 +77,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -165,7 +164,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
ParallelFediversePosts = 1
|
||||
};
|
||||
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
var removeFollowerMock = new Mock<IRemoveFollowerAction>(MockBehavior.Strict);
|
||||
#endregion
|
||||
|
||||
|
@ -250,7 +248,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -343,7 +340,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
ParallelFediversePosts = 1
|
||||
|
@ -440,7 +436,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
ParallelFediversePosts = 1
|
||||
|
@ -519,7 +514,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -600,7 +594,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -689,7 +682,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -775,7 +767,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -865,7 +856,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -959,7 +949,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
@ -1054,7 +1043,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
|
|||
.Returns(Task.CompletedTask);
|
||||
|
||||
var loggerMock = new Mock<ILogger<SendTweetsToFollowersProcessor>>();
|
||||
var saveProgressMock = new Mock<ISaveProgressionTask>();
|
||||
|
||||
var settings = new InstanceSettings
|
||||
{
|
||||
|
|
|
@ -57,7 +57,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
@ -139,7 +138,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings { };
|
||||
|
@ -218,7 +216,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
@ -301,7 +298,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
@ -375,7 +371,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 10 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
@ -456,7 +451,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 10 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
@ -560,7 +554,6 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
InboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
};
|
||||
|
||||
var settings = new InstanceSettings
|
||||
|
|
|
@ -61,21 +61,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -161,21 +158,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -262,21 +256,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -350,21 +341,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 10}}
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 8}}
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 7}}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -447,21 +435,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 10}}
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 8}}
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 7}}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -568,21 +553,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -648,21 +630,18 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
|
|||
Id = 1,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 2,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
|
||||
},
|
||||
new Follower
|
||||
{
|
||||
Id = 3,
|
||||
Host = host,
|
||||
SharedInboxRoute = inbox,
|
||||
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -30,18 +30,16 @@ namespace BirdsiteLive.Pipeline.Tests
|
|||
var retrieveTweetsProcessor = new Mock<IRetrieveTweetsProcessor>(MockBehavior.Strict);
|
||||
var retrieveFollowersProcessor = new Mock<IRetrieveFollowersProcessor>(MockBehavior.Strict);
|
||||
var sendTweetsToFollowersProcessor = new Mock<ISendTweetsToFollowersProcessor>(MockBehavior.Strict);
|
||||
var saveProgressionProcessor = new Mock<ISaveProgressionTask>(MockBehavior.Strict);
|
||||
var logger = new Mock<ILogger<StatusPublicationPipeline>>();
|
||||
#endregion
|
||||
|
||||
var pipeline = new StatusPublicationPipeline(retrieveTweetsProcessor.Object, retrieveTwitterUserProcessor.Object, retrieveFollowersProcessor.Object, sendTweetsToFollowersProcessor.Object, saveProgressionProcessor.Object, logger.Object);
|
||||
var pipeline = new StatusPublicationPipeline(retrieveTweetsProcessor.Object, retrieveTwitterUserProcessor.Object, retrieveFollowersProcessor.Object, sendTweetsToFollowersProcessor.Object, logger.Object);
|
||||
await pipeline.ExecuteAsync(ct.Token);
|
||||
|
||||
#region Validations
|
||||
retrieveTweetsProcessor.VerifyAll();
|
||||
retrieveFollowersProcessor.VerifyAll();
|
||||
sendTweetsToFollowersProcessor.VerifyAll();
|
||||
saveProgressionProcessor.VerifyAll();
|
||||
logger.VerifyAll();
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
Reference in a new issue