init dataAccessLayers

This commit is contained in:
Nicolas Constant 2020-07-05 20:22:34 -04:00
parent 0249df0b9f
commit b34385fd22
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688
19 changed files with 461 additions and 3 deletions

View file

@ -15,6 +15,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Launch Db for testing
run: docker run --name postgres -e POSTGRES_DB=mytestdb -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:

View file

@ -17,11 +17,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{A32D3458
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Cryptography.Tests", "Tests\BirdsiteLive.Cryptography.Tests\BirdsiteLive.Cryptography.Tests.csproj", "{155D46A4-2D05-47F2-8FFC-0B7C412A7652}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.Domain", "BirdsiteLive.Domain\BirdsiteLive.Domain.csproj", "{D48450EE-D8BD-4228-9864-043AC88F7EE0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Domain", "BirdsiteLive.Domain\BirdsiteLive.Domain.csproj", "{D48450EE-D8BD-4228-9864-043AC88F7EE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataAccessLayers", "DataAccessLayers", "{CFAB3509-3931-42DB-AC97-4F91FC2D849C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL", "DataAccessLayers\BirdsiteLive.DAL\BirdsiteLive.DAL.csproj", "{47058CAB-DC43-4DD1-8F68-D3D625332905}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL.Postgres", "DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj", "{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL.Postgres.Tests", "Tests\BirdsiteLive.DAL.Postgres.Tests\BirdsiteLive.DAL.Postgres.Tests.csproj", "{CD9489BF-69C8-4705-8774-81C45F4F8FE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -61,6 +69,18 @@ Global
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.Build.0 = Release|Any CPU
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Release|Any CPU.Build.0 = Release|Any CPU
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Release|Any CPU.Build.0 = Release|Any CPU
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -73,6 +93,9 @@ Global
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
{1D713961-9926-41FF-8D6A-8A4B8D548484} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
{47058CAB-DC43-4DD1-8F68-D3D625332905} = {CFAB3509-3931-42DB-AC97-4F91FC2D849C}
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6} = {CFAB3509-3931-42DB-AC97-4F91FC2D849C}
{CD9489BF-69C8-4705-8774-81C45F4F8FE1} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Npgsql" Version="4.1.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BirdsiteLive.DAL\BirdsiteLive.DAL.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,25 @@
using BirdsiteLive.DAL.Postgres.Settings;
using Npgsql;
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers.Base
{
public class PostgresBase
{
protected readonly PostgresSettings _settings;
#region Ctor
protected PostgresBase(PostgresSettings settings)
{
_settings = settings;
}
#endregion
protected NpgsqlConnection Connection
{
get
{
return new NpgsqlConnection(_settings.ConnString);
}
}
}
}

View file

@ -0,0 +1,150 @@
using System;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Xml;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base;
using BirdsiteLive.DAL.Postgres.Settings;
using BirdsiteLive.DAL.Postgres.Tools;
using Dapper;
using Npgsql;
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
{
internal class DbVersion
{
public string Type { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
}
public class DbInitializerPostgresDal : PostgresBase, IDbInitializerDal
{
private readonly PostgresTools _tools;
private readonly Version _currentVersion = new Version(1,0);
private const string DbVersionType = "db-version";
#region Ctor
public DbInitializerPostgresDal(PostgresSettings settings, PostgresTools tools) : base(settings)
{
_tools = tools;
}
#endregion
public async Task<Version> GetCurrentDbVersionAsync()
{
var query = $"SELECT * FROM {_settings.DbVersionTableName} WHERE type = @type";
try
{
using (var dbConnection = Connection)
{
dbConnection.Open();
var result = (await dbConnection.QueryAsync<DbVersion>(query, new { type = DbVersionType })).FirstOrDefault();
if (result == default)
return null;
return new Version(result.Major, result.Minor);
}
}
catch (PostgresException e)
{
if (e.Message.Contains("42P01"))
return null;
throw;
}
}
public Version GetMandatoryDbVersion()
{
return _currentVersion;
}
public Tuple<Version, Version>[] GetMigrationPatterns()
{
return new Tuple<Version, Version>[0];
}
public Task MigrateDbAsync(Version from, Version to)
{
throw new NotImplementedException();
}
public async Task InitDbAsync()
{
// Create version table
var createVersion = $@"CREATE TABLE {_settings.DbVersionTableName}
(
type VARCHAR(128) PRIMARY KEY,
major SMALLINT NOT NULL,
minor SMALLINT NOT NULL
);";
await _tools.ExecuteRequestAsync(createVersion);
// Create Twitter User table
var createTwitter = $@"CREATE TABLE {_settings.TwitterUserTableName}
(
id SERIAL PRIMARY KEY,
acct VARCHAR(20) UNIQUE,
lastTweetPostedId BIGINT,
lastTweetSynchronizedForAllFollowersId BIGINT
);";
await _tools.ExecuteRequestAsync(createTwitter);
// Create Followers table
var createFollowers = $@"CREATE TABLE {_settings.FollowersTableName}
(
id SERIAL PRIMARY KEY,
followings INTEGER[],
followingsSyncStatus JSONB,
acct VARCHAR(20) UNIQUE,
host VARCHAR(20)
);";
await _tools.ExecuteRequestAsync(createFollowers);
// Create Cached Tweet table
var createCachedTweets = $@"CREATE TABLE {_settings.CachedTweetsTableName}
(
id BIGINT PRIMARY KEY,
twitterUserId INTEGER,
data JSONB
);";
await _tools.ExecuteRequestAsync(createCachedTweets);
// Insert version to db
using (var dbConnection = Connection)
{
dbConnection.Open();
await dbConnection.ExecuteAsync(
$"INSERT INTO {_settings.DbVersionTableName} (type,major,minor) VALUES(@type,@major,@minor)",
new { type = DbVersionType, major = _currentVersion.Major, minor = _currentVersion.Minor });
}
}
public async Task DeleteAllAsync()
{
var dropsRequests = new[]
{
$@"DROP TABLE {_settings.DbVersionTableName};",
$@"DROP TABLE {_settings.TwitterUserTableName};",
$@"DROP TABLE {_settings.FollowersTableName};",
$@"DROP TABLE {_settings.CachedTweetsTableName};"
};
foreach (var r in dropsRequests)
{
await _tools.ExecuteRequestAsync(r);
}
}
}
}

View file

@ -0,0 +1,9 @@
using BirdsiteLive.DAL.Contracts;
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
{
public class FollowersPostgresDal : IFollowersDal
{
}
}

View file

@ -0,0 +1,9 @@
using BirdsiteLive.DAL.Contracts;
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
{
public class TwitterUserPostgresDal : ITwitterUserDal
{
}
}

View file

@ -0,0 +1,12 @@
namespace BirdsiteLive.DAL.Postgres.Settings
{
public class PostgresSettings
{
public string ConnString { get; set; }
public string DbVersionTableName { get; set; } = "db-version";
public string TwitterUserTableName { get; set; } = "twitter-users";
public string FollowersTableName { get; set; } = "followers";
public string CachedTweetsTableName { get; set; } = "cached-tweets";
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using BirdsiteLive.DAL.Postgres.Settings;
using Npgsql;
namespace BirdsiteLive.DAL.Postgres.Tools
{
public class PostgresTools
{
private readonly PostgresSettings _settings;
#region Ctor
public PostgresTools(PostgresSettings settings)
{
_settings = settings;
}
#endregion
public async Task ExecuteRequestAsync(string request)
{
try
{
using (var conn = new NpgsqlConnection(_settings.ConnString))
using (var cmd = new NpgsqlCommand(request, conn))
{
await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}

View file

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,15 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace BirdsiteLive.DAL.Contracts
{
public interface IDbInitializerDal
{
Task<Version> GetCurrentDbVersionAsync();
Version GetMandatoryDbVersion();
Tuple<Version, Version>[] GetMigrationPatterns();
Task MigrateDbAsync(Version from, Version to);
Task InitDbAsync();
}
}

View file

@ -0,0 +1,7 @@
namespace BirdsiteLive.DAL.Contracts
{
public interface IFollowersDal
{
}
}

View file

@ -0,0 +1,7 @@
namespace BirdsiteLive.DAL.Contracts
{
public interface ITwitterUserDal
{
}
}

View file

@ -0,0 +1,10 @@
namespace BirdsiteLive.DAL.Models
{
public class CachedTweet
{
public long Id { get; set; }
public long TwitterUserId { get; set; }
public string TweetData { get; set; }
}
}

View file

@ -0,0 +1,13 @@
namespace BirdsiteLive.DAL.Models
{
public class Follower
{
public int Id { get; set; }
public int FollowingAccountId { get; set; }
public string Acct { get; set; }
public string Host { get; set; }
public long LastTweetSynchronizedId { get; set; }
}
}

View file

@ -0,0 +1,11 @@
namespace BirdsiteLive.DAL.Models
{
public class SyncTwitterUser
{
public int Id { get; set; }
public string Acct { get; set; }
public long LastTweetPostedId { get; set; }
public long LastTweetSynchronizedForAllFollowersId { get; set; }
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Linq;
namespace BirdsiteLive.DAL.Tools
{
public static class RandomGenerator
{
private static readonly Random Random = new Random();
public static string GetString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[Random.Next(s.Length)]).ToArray());
}
}
}

View file

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using BirdsiteLive.DAL.Postgres.DataAccessLayers;
using BirdsiteLive.DAL.Postgres.Settings;
using BirdsiteLive.DAL.Postgres.Tools;
using BirdsiteLive.DAL.Tools;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
{
[TestClass]
public class DbInitializerPostgresDalTests
{
private readonly PostgresSettings _settings;
private readonly PostgresTools _tools;
#region Ctor
public DbInitializerPostgresDalTests()
{
_settings = new PostgresSettings
{
ConnString = "Host=127.0.0.1;Username=postgres;Password=mysecretpassword;Database=mytestdb",
DbVersionTableName = "DbVersionTableName" + RandomGenerator.GetString(4),
CachedTweetsTableName = "CachedTweetsTableName" + RandomGenerator.GetString(4),
FollowersTableName = "FollowersTableName" + RandomGenerator.GetString(4),
TwitterUserTableName = "TwitterUserTableName" + RandomGenerator.GetString(4),
};
_tools = new PostgresTools(_settings);
}
#endregion
[TestCleanup]
public async Task CleanUp()
{
var dal = new DbInitializerPostgresDal(_settings, _tools);
try
{
await dal.DeleteAllAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
[TestMethod]
public async Task GetCurrentDbVersionAsync_UninitializedDb()
{
var dal = new DbInitializerPostgresDal(_settings, _tools);
var current = await dal.GetCurrentDbVersionAsync();
Assert.IsNull(current);
}
[TestMethod]
public async Task InitDbAsync()
{
var dal = new DbInitializerPostgresDal(_settings, _tools);
await dal.InitDbAsync();
var current = await dal.GetCurrentDbVersionAsync();
var mandatory = dal.GetMandatoryDbVersion();
Assert.IsNotNull(current);
Assert.AreEqual(mandatory.Minor, current.Minor);
Assert.AreEqual(mandatory.Major, current.Major);
}
}
}