feat: new migrator
This commit is contained in:
parent
b36b54f9e6
commit
79b8c4799e
17 changed files with 621 additions and 917 deletions
20
Foxnouns.DataMigrator/Foxnouns.DataMigrator.csproj
Normal file
20
Foxnouns.DataMigrator/Foxnouns.DataMigrator.csproj
Normal file
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Foxnouns.Backend\Foxnouns.Backend.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.1.35"/>
|
||||
<PackageReference Include="Npgsql" Version="9.0.2"/>
|
||||
<PackageReference Include="Npgsql.NodaTime" Version="9.0.2"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
71
Foxnouns.DataMigrator/GoDatabase.cs
Normal file
71
Foxnouns.DataMigrator/GoDatabase.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System.Data;
|
||||
using Dapper;
|
||||
using Foxnouns.DataMigrator.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Npgsql;
|
||||
|
||||
namespace Foxnouns.DataMigrator;
|
||||
|
||||
public static class GoDatabase
|
||||
{
|
||||
private static NpgsqlDataSource? _dataSource;
|
||||
|
||||
public static async Task<NpgsqlConnection> GetConnectionAsync()
|
||||
{
|
||||
if (_dataSource != null)
|
||||
return await _dataSource.OpenConnectionAsync();
|
||||
|
||||
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
|
||||
SqlMapper.RemoveTypeMap(typeof(ulong));
|
||||
SqlMapper.AddTypeHandler(new UlongEncodeAsLongHandler());
|
||||
SqlMapper.AddTypeHandler(new JsonTypeHandler<GoFieldEntry[]>());
|
||||
SqlMapper.AddTypeHandler(new JsonTypeHandler<Dictionary<string, GoCustomPreference>>());
|
||||
SqlMapper.AddTypeHandler(new JsonTypeHandler<GoPronounEntry[]>());
|
||||
SqlMapper.AddTypeHandler(new UlongListHandler());
|
||||
|
||||
string dsn =
|
||||
Environment.GetEnvironmentVariable("GO_DATABASE")
|
||||
?? throw new Exception("$GO_DATABASE is not set");
|
||||
|
||||
var dataSourceBuilder = new NpgsqlDataSourceBuilder(dsn);
|
||||
dataSourceBuilder.UseJsonNet();
|
||||
|
||||
_dataSource = dataSourceBuilder.Build();
|
||||
|
||||
return await _dataSource.OpenConnectionAsync();
|
||||
}
|
||||
|
||||
// dapper why
|
||||
// taken from https://codeberg.org/starshine/catalogger/src/branch/main/Catalogger.Backend/Database/DatabasePool.cs
|
||||
private class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value) =>
|
||||
parameter.Value = (long)value;
|
||||
|
||||
public override ulong Parse(object value) =>
|
||||
// Cast to long to unbox, then to ulong (???)
|
||||
(ulong)(long)value;
|
||||
}
|
||||
|
||||
private class UlongListHandler : SqlMapper.TypeHandler<List<ulong>>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, List<ulong>? value) =>
|
||||
parameter.Value = value?.Select(i => (long)i).ToArray();
|
||||
|
||||
public override List<ulong>? Parse(object value) =>
|
||||
((long[])value).Select(i => (ulong)i).ToList();
|
||||
}
|
||||
|
||||
private class JsonTypeHandler<T> : SqlMapper.TypeHandler<T>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, T? value) =>
|
||||
parameter.Value = JsonConvert.SerializeObject(value);
|
||||
|
||||
public override T? Parse(object value)
|
||||
{
|
||||
var json = (string)value;
|
||||
return JsonConvert.DeserializeObject<T>(json) ?? default;
|
||||
}
|
||||
}
|
||||
}
|
26
Foxnouns.DataMigrator/Models/GoMember.cs
Normal file
26
Foxnouns.DataMigrator/Models/GoMember.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using Foxnouns.Backend.Database;
|
||||
|
||||
namespace Foxnouns.DataMigrator.Models;
|
||||
|
||||
public class GoMember
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public string? Bio { get; init; }
|
||||
public string[]? Links { get; init; }
|
||||
public string? DisplayName { get; init; }
|
||||
public GoFieldEntry[] Names { get; init; } = [];
|
||||
public GoPronounEntry[] Pronouns { get; init; } = [];
|
||||
public string? Avatar { get; init; }
|
||||
public required bool Unlisted { get; init; }
|
||||
public required string Sid { get; init; }
|
||||
public required Snowflake SnowflakeId { get; init; }
|
||||
}
|
||||
|
||||
public class GoMemberField
|
||||
{
|
||||
public required string MemberId { get; init; }
|
||||
public required long Id { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required GoFieldEntry[] Entries { get; init; }
|
||||
}
|
102
Foxnouns.DataMigrator/Models/GoUser.cs
Normal file
102
Foxnouns.DataMigrator/Models/GoUser.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
|
||||
namespace Foxnouns.DataMigrator.Models;
|
||||
|
||||
public class GoUser
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string Username { get; init; }
|
||||
public string? DisplayName { get; init; }
|
||||
public string? Bio { get; init; }
|
||||
public string[]? Links { get; init; }
|
||||
public string? Discord { get; init; }
|
||||
public string? DiscordUsername { get; init; }
|
||||
public DateTimeOffset? DeletedAt { get; init; }
|
||||
public bool? SelfDelete { get; init; }
|
||||
public string? DeleteReason { get; init; }
|
||||
public GoFieldEntry[] Names { get; init; } = [];
|
||||
public GoPronounEntry[] Pronouns { get; init; } = [];
|
||||
public string? Avatar { get; init; }
|
||||
public string? Fediverse { get; init; }
|
||||
public string? FediverseUsername { get; init; }
|
||||
public int? FediverseAppId { get; init; }
|
||||
public bool IsAdmin { get; init; }
|
||||
public string? MemberTitle { get; init; }
|
||||
public bool ListPrivate { get; init; }
|
||||
public string? Tumblr { get; init; }
|
||||
public string? TumblrUsername { get; init; }
|
||||
public string? Google { get; init; }
|
||||
public string? GoogleUsername { get; init; }
|
||||
public Dictionary<string, GoCustomPreference> CustomPreferences { get; init; } = [];
|
||||
public DateTimeOffset LastActive { get; init; }
|
||||
public required string Sid { get; init; }
|
||||
public DateTimeOffset LastSidReroll { get; init; }
|
||||
public string? Timezone { get; init; }
|
||||
public Snowflake SnowflakeId { get; init; }
|
||||
}
|
||||
|
||||
public class GoUserField
|
||||
{
|
||||
public required string UserId { get; init; }
|
||||
public required long Id { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required GoFieldEntry[] Entries { get; init; }
|
||||
}
|
||||
|
||||
public class GoPrideFlag
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string UserId { get; init; }
|
||||
public required string Hash { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public required Snowflake SnowflakeId { get; init; }
|
||||
}
|
||||
|
||||
public class GoProfileFlag
|
||||
{
|
||||
public string? UserId { get; init; }
|
||||
public string? MemberId { get; init; }
|
||||
public required long Id { get; init; }
|
||||
public required string FlagId { get; init; }
|
||||
}
|
||||
|
||||
public class GoFediverseApp
|
||||
{
|
||||
public required int Id { get; init; }
|
||||
public required string Instance { get; init; }
|
||||
public required string ClientId { get; init; }
|
||||
public required string ClientSecret { get; init; }
|
||||
public required string InstanceType { get; init; }
|
||||
|
||||
public FediverseInstanceType TypeToEnum() =>
|
||||
InstanceType switch
|
||||
{
|
||||
"sharkey" => FediverseInstanceType.MisskeyApi,
|
||||
"firefish" => FediverseInstanceType.MisskeyApi,
|
||||
"pixelfed" => FediverseInstanceType.MastodonApi,
|
||||
"mastodon" => FediverseInstanceType.MastodonApi,
|
||||
"pleroma" => FediverseInstanceType.MastodonApi,
|
||||
"akkoma" => FediverseInstanceType.MastodonApi,
|
||||
"misskey" => FediverseInstanceType.MastodonApi,
|
||||
"gotosocial" => FediverseInstanceType.MastodonApi,
|
||||
"calckey" => FediverseInstanceType.MisskeyApi,
|
||||
"foundkey" => FediverseInstanceType.MisskeyApi,
|
||||
// this should never happen but if it does we just fall back to the mastodon api
|
||||
// basically everything but misskey forks implement it anyway :3
|
||||
_ => FediverseInstanceType.MastodonApi,
|
||||
};
|
||||
}
|
||||
|
||||
public record GoFieldEntry(string Value, string Status);
|
||||
|
||||
public record GoPronounEntry(string Pronouns, string? DisplayText, string Status);
|
||||
|
||||
public record GoCustomPreference(
|
||||
string Icon,
|
||||
string Tooltip,
|
||||
string PreferenceSize,
|
||||
bool Muted,
|
||||
bool Favourite
|
||||
);
|
101
Foxnouns.DataMigrator/Program.cs
Normal file
101
Foxnouns.DataMigrator/Program.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using System.Globalization;
|
||||
using Foxnouns.Backend;
|
||||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Foxnouns.Backend.Extensions;
|
||||
using Foxnouns.DataMigrator.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Npgsql;
|
||||
using Serilog;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
|
||||
namespace Foxnouns.DataMigrator;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
// Create logger and get configuration
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.WriteTo.Console(theme: AnsiConsoleTheme.Sixteen)
|
||||
.CreateLogger();
|
||||
|
||||
Config config =
|
||||
new ConfigurationBuilder()
|
||||
.AddConfiguration()
|
||||
.Build()
|
||||
// Get the configuration as our config class
|
||||
.Get<Config>() ?? new Config();
|
||||
|
||||
NpgsqlConnection conn = await GoDatabase.GetConnectionAsync();
|
||||
// just reuse the design time factory so we don't have to copy this
|
||||
DatabaseContext context = new DesignTimeDatabaseContextFactory().CreateDbContext(args);
|
||||
|
||||
await context.Database.MigrateAsync();
|
||||
|
||||
Log.Information("Migrating applications");
|
||||
Dictionary<int, Snowflake> appIds = await MigrateAppsAsync(conn, context);
|
||||
|
||||
Log.Information("Migrating users");
|
||||
List<GoUser> users = await Queries.GetUsersAsync(conn);
|
||||
List<GoUserField> userFields = await Queries.GetUserFieldsAsync(conn);
|
||||
List<GoMemberField> memberFields = await Queries.GetMemberFieldsAsync(conn);
|
||||
List<GoPrideFlag> prideFlags = await Queries.GetUserFlagsAsync(conn);
|
||||
List<GoProfileFlag> userFlags = await Queries.GetUserProfileFlagsAsync(conn);
|
||||
List<GoProfileFlag> memberFlags = await Queries.GetMemberProfileFlagsAsync(conn);
|
||||
Log.Information("Migrating {Count} users", users.Count);
|
||||
foreach ((GoUser user, int i) in users.Select((user, i) => (user, i)))
|
||||
{
|
||||
Log.Debug(
|
||||
"Migrating user #{Index}/{Count}: {Id}/{SnowflakeId}",
|
||||
i,
|
||||
users.Count,
|
||||
user.Id,
|
||||
user.SnowflakeId
|
||||
);
|
||||
await new UserMigrator(
|
||||
conn,
|
||||
context,
|
||||
user,
|
||||
appIds,
|
||||
userFields,
|
||||
memberFields,
|
||||
prideFlags,
|
||||
userFlags,
|
||||
memberFlags
|
||||
).MigrateAsync();
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Log.Information("Migration complete!");
|
||||
}
|
||||
|
||||
private static async Task<Dictionary<int, Snowflake>> MigrateAppsAsync(
|
||||
NpgsqlConnection conn,
|
||||
DatabaseContext context
|
||||
)
|
||||
{
|
||||
List<GoFediverseApp> goApps = await Queries.GetFediverseAppsAsync(conn);
|
||||
var appIds = new Dictionary<int, Snowflake>();
|
||||
foreach (GoFediverseApp app in goApps)
|
||||
{
|
||||
Log.Debug("Migrating application for {Domain}", app.Instance);
|
||||
Snowflake id = SnowflakeGenerator.Instance.GenerateSnowflake();
|
||||
appIds[app.Id] = id;
|
||||
context.FediverseApplications.Add(
|
||||
new FediverseApplication
|
||||
{
|
||||
Id = id,
|
||||
Domain = app.Instance.ToLower(CultureInfo.InvariantCulture),
|
||||
ClientId = app.ClientId,
|
||||
ClientSecret = app.ClientSecret,
|
||||
InstanceType = app.TypeToEnum(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return appIds;
|
||||
}
|
||||
}
|
34
Foxnouns.DataMigrator/Queries.cs
Normal file
34
Foxnouns.DataMigrator/Queries.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using Coravel.Mailer.Mail.Helpers;
|
||||
using Dapper;
|
||||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Foxnouns.DataMigrator.Models;
|
||||
using NodaTime.Extensions;
|
||||
using Npgsql;
|
||||
|
||||
namespace Foxnouns.DataMigrator;
|
||||
|
||||
public static class Queries
|
||||
{
|
||||
public static async Task<List<GoFediverseApp>> GetFediverseAppsAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoFediverseApp>("select * from fediverse_apps")).ToList();
|
||||
|
||||
public static async Task<List<GoUser>> GetUsersAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoUser>("select * from users order by id")).ToList();
|
||||
|
||||
public static async Task<List<GoUserField>> GetUserFieldsAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoUserField>("select * from user_fields order by id")).ToList();
|
||||
|
||||
public static async Task<List<GoMemberField>> GetMemberFieldsAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoMemberField>("select * from member_fields order by id")).ToList();
|
||||
|
||||
public static async Task<List<GoProfileFlag>> GetUserProfileFlagsAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoProfileFlag>("select * from user_flags order by id")).ToList();
|
||||
|
||||
public static async Task<List<GoProfileFlag>> GetMemberProfileFlagsAsync(
|
||||
NpgsqlConnection conn
|
||||
) => (await conn.QueryAsync<GoProfileFlag>("select * from member_flags order by id")).ToList();
|
||||
|
||||
public static async Task<List<GoPrideFlag>> GetUserFlagsAsync(NpgsqlConnection conn) =>
|
||||
(await conn.QueryAsync<GoPrideFlag>("select * from pride_flags order by id")).ToList();
|
||||
}
|
261
Foxnouns.DataMigrator/UserMigrator.cs
Normal file
261
Foxnouns.DataMigrator/UserMigrator.cs
Normal file
|
@ -0,0 +1,261 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapper;
|
||||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Foxnouns.Backend.Utils;
|
||||
using Foxnouns.DataMigrator.Models;
|
||||
using NodaTime.Extensions;
|
||||
using Npgsql;
|
||||
using Serilog;
|
||||
|
||||
namespace Foxnouns.DataMigrator;
|
||||
|
||||
public class UserMigrator(
|
||||
NpgsqlConnection conn,
|
||||
DatabaseContext context,
|
||||
GoUser goUser,
|
||||
Dictionary<int, Snowflake> fediverseApplicationIds,
|
||||
List<GoUserField> userFields,
|
||||
List<GoMemberField> memberFields,
|
||||
List<GoPrideFlag> prideFlags,
|
||||
List<GoProfileFlag> userFlags,
|
||||
List<GoProfileFlag> memberFlags
|
||||
)
|
||||
{
|
||||
private readonly Dictionary<string, Snowflake> _preferenceIds = new();
|
||||
private readonly Dictionary<string, Snowflake> _flagIds = new();
|
||||
private User? _user;
|
||||
|
||||
public async Task MigrateAsync()
|
||||
{
|
||||
CreateNewUser();
|
||||
MigrateFlags();
|
||||
await MigrateMembersAsync();
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_user))]
|
||||
private void CreateNewUser()
|
||||
{
|
||||
_user = new User
|
||||
{
|
||||
Id = goUser.SnowflakeId,
|
||||
Username = goUser.Username,
|
||||
DisplayName = goUser.DisplayName,
|
||||
Bio = goUser.Bio,
|
||||
Links = goUser.Links ?? [],
|
||||
|
||||
Deleted = goUser.DeletedAt != null,
|
||||
DeletedAt = goUser.DeletedAt?.ToInstant(),
|
||||
DeletedBy = goUser.SelfDelete == true ? null : goUser.SnowflakeId,
|
||||
|
||||
Names = goUser.Names.Select(ConvertFieldEntry).ToList(),
|
||||
Pronouns = goUser.Pronouns.Select(ConvertPronoun).ToList(),
|
||||
Avatar = goUser.Avatar,
|
||||
|
||||
Role = goUser.IsAdmin ? UserRole.Admin : UserRole.User,
|
||||
MemberTitle = goUser.MemberTitle,
|
||||
ListHidden = goUser.ListPrivate,
|
||||
CustomPreferences = ConvertPreferences(),
|
||||
LastActive = goUser.LastActive.ToInstant(),
|
||||
Sid = goUser.Sid,
|
||||
LastSidReroll = goUser.LastSidReroll.ToInstant(),
|
||||
Timezone = goUser.Timezone,
|
||||
Fields = userFields
|
||||
.Where(f => f.UserId == goUser.Id)
|
||||
.Select(f => new Field
|
||||
{
|
||||
Name = f.Name,
|
||||
Entries = f.Entries.Select(ConvertFieldEntry).ToArray(),
|
||||
})
|
||||
.ToList(),
|
||||
};
|
||||
context.Users.Add(_user);
|
||||
|
||||
// Create the user's auth methods
|
||||
if (goUser.Discord != null)
|
||||
{
|
||||
context.AuthMethods.Add(
|
||||
new AuthMethod
|
||||
{
|
||||
Id = SnowflakeGenerator.Instance.GenerateSnowflake(_user.Id.Time),
|
||||
UserId = _user.Id,
|
||||
AuthType = AuthType.Discord,
|
||||
RemoteId = goUser.Discord,
|
||||
RemoteUsername = goUser.DiscordUsername ?? "(unknown)",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (goUser.Google != null)
|
||||
{
|
||||
context.AuthMethods.Add(
|
||||
new AuthMethod
|
||||
{
|
||||
Id = SnowflakeGenerator.Instance.GenerateSnowflake(_user.Id.Time),
|
||||
UserId = _user.Id,
|
||||
AuthType = AuthType.Google,
|
||||
RemoteId = goUser.Google,
|
||||
RemoteUsername = goUser.GoogleUsername ?? "(unknown)",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (goUser.Tumblr != null)
|
||||
{
|
||||
context.AuthMethods.Add(
|
||||
new AuthMethod
|
||||
{
|
||||
Id = SnowflakeGenerator.Instance.GenerateSnowflake(_user.Id.Time),
|
||||
UserId = _user.Id,
|
||||
AuthType = AuthType.Tumblr,
|
||||
RemoteId = goUser.Tumblr,
|
||||
RemoteUsername = goUser.TumblrUsername ?? "(unknown)",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (goUser.Fediverse != null)
|
||||
{
|
||||
context.AuthMethods.Add(
|
||||
new AuthMethod
|
||||
{
|
||||
Id = SnowflakeGenerator.Instance.GenerateSnowflake(_user.Id.Time),
|
||||
UserId = _user.Id,
|
||||
AuthType = AuthType.Fediverse,
|
||||
RemoteId = goUser.Fediverse,
|
||||
RemoteUsername = goUser.FediverseUsername ?? "(unknown)",
|
||||
FediverseApplicationId = fediverseApplicationIds[goUser.FediverseAppId!.Value],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateFlags()
|
||||
{
|
||||
foreach (GoPrideFlag flag in prideFlags.Where(f => f.UserId == goUser.Id))
|
||||
{
|
||||
_flagIds[flag.Id] = flag.SnowflakeId;
|
||||
context.PrideFlags.Add(
|
||||
new PrideFlag
|
||||
{
|
||||
Id = flag.SnowflakeId,
|
||||
UserId = _user!.Id,
|
||||
Hash = flag.Hash,
|
||||
Name = flag.Name,
|
||||
Description = flag.Description,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
context.UserFlags.AddRange(
|
||||
userFlags
|
||||
.Where(f => f.UserId == goUser.Id)
|
||||
.Where(f => _flagIds.ContainsKey(f.FlagId))
|
||||
.Select(f => new UserFlag { UserId = _user!.Id, PrideFlagId = _flagIds[f.FlagId] })
|
||||
);
|
||||
}
|
||||
|
||||
private async Task MigrateMembersAsync()
|
||||
{
|
||||
List<GoMember> members = (
|
||||
await conn.QueryAsync<GoMember>(
|
||||
"select * from members where user_id = @Id",
|
||||
new { goUser.Id }
|
||||
)
|
||||
).ToList();
|
||||
|
||||
foreach (GoMember member in members)
|
||||
{
|
||||
Log.Debug("Migrating member {Id}/{SnowflakeId}", member.Id, member.SnowflakeId);
|
||||
MigrateMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateMember(GoMember goMember)
|
||||
{
|
||||
var names = goMember.Names.Select(ConvertFieldEntry).ToList();
|
||||
var pronouns = goMember.Pronouns.Select(ConvertPronoun).ToList();
|
||||
var fields = memberFields
|
||||
.Where(f => f.MemberId == goMember.Id)
|
||||
.Select(f => new Field
|
||||
{
|
||||
Name = f.Name,
|
||||
Entries = f.Entries.Select(ConvertFieldEntry).ToArray(),
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var member = new Member
|
||||
{
|
||||
Id = goMember.SnowflakeId,
|
||||
UserId = _user!.Id,
|
||||
Name = goMember.Name,
|
||||
Sid = goMember.Sid,
|
||||
DisplayName = goMember.DisplayName,
|
||||
Bio = goMember.Bio,
|
||||
Avatar = goMember.Avatar,
|
||||
Links = goMember.Links ?? [],
|
||||
Unlisted = goMember.Unlisted,
|
||||
Names = names,
|
||||
Pronouns = pronouns,
|
||||
Fields = fields,
|
||||
};
|
||||
context.Members.Add(member);
|
||||
|
||||
context.MemberFlags.AddRange(
|
||||
memberFlags
|
||||
.Where(f => f.MemberId == goMember.Id)
|
||||
.Select(f => new MemberFlag
|
||||
{
|
||||
MemberId = member.Id,
|
||||
PrideFlagId = _flagIds[f.FlagId],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private Dictionary<Snowflake, User.CustomPreference> ConvertPreferences()
|
||||
{
|
||||
var prefs = new Dictionary<Snowflake, User.CustomPreference>();
|
||||
|
||||
foreach ((string id, GoCustomPreference goPref) in goUser.CustomPreferences)
|
||||
{
|
||||
Snowflake newId = SnowflakeGenerator.Instance.GenerateSnowflake(
|
||||
goUser.SnowflakeId.Time
|
||||
);
|
||||
_preferenceIds[id] = newId;
|
||||
prefs[newId] = new User.CustomPreference
|
||||
{
|
||||
Icon = goPref.Icon,
|
||||
Tooltip = goPref.Tooltip,
|
||||
Muted = goPref.Muted,
|
||||
Favourite = goPref.Favourite,
|
||||
Size = goPref.PreferenceSize switch
|
||||
{
|
||||
"large" => PreferenceSize.Large,
|
||||
"normal" => PreferenceSize.Normal,
|
||||
"small" => PreferenceSize.Small,
|
||||
_ => PreferenceSize.Normal,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
private FieldEntry ConvertFieldEntry(GoFieldEntry entry) =>
|
||||
new() { Value = entry.Value, Status = ConvertPreferenceId(entry.Status) };
|
||||
|
||||
private Pronoun ConvertPronoun(GoPronounEntry entry) =>
|
||||
new()
|
||||
{
|
||||
Value = entry.Pronouns,
|
||||
DisplayText = entry.DisplayText,
|
||||
Status = ConvertPreferenceId(entry.Status),
|
||||
};
|
||||
|
||||
private string ConvertPreferenceId(string id)
|
||||
{
|
||||
if (_preferenceIds.TryGetValue(id, out Snowflake preferenceId))
|
||||
return preferenceId.ToString();
|
||||
return ValidationUtils.DefaultStatusOptions.Contains(id) ? id : "okay";
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue