Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data resolver #92

Merged
merged 4 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 21 additions & 24 deletions src/GrillBot.App/Actions/Api/V1/AuditLog/GetAuditLogDetail.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Text.Json;
using AutoMapper;
using GrillBot.Common.Extensions.Discord;
using GrillBot.App.Managers.DataResolve;
using GrillBot.Common.Models;
using GrillBot.Core.Extensions;
using GrillBot.Core.Infrastructure.Actions;
Expand All @@ -13,17 +12,13 @@ namespace GrillBot.App.Actions.Api.V1.AuditLog;
public class GetAuditLogDetail : ApiAction
{
private IAuditLogServiceClient AuditLogServiceClient { get; }
private GrillBotDatabaseBuilder DatabaseBuilder { get; }
private IDiscordClient DiscordClient { get; }
private IMapper Mapper { get; }

public GetAuditLogDetail(ApiRequestContext apiContext, IAuditLogServiceClient auditLogServiceClient, GrillBotDatabaseBuilder databaseBuilder, IDiscordClient discordClient,
IMapper mapper) : base(apiContext)
private readonly DataResolveManager _dataResolve;

public GetAuditLogDetail(ApiRequestContext apiContext, IAuditLogServiceClient auditLogServiceClient, DataResolveManager dataResolve) : base(apiContext)
{
AuditLogServiceClient = auditLogServiceClient;
DatabaseBuilder = databaseBuilder;
DiscordClient = discordClient;
Mapper = mapper;
_dataResolve = dataResolve;
}

public override async Task<ApiResult> ProcessAsync()
Expand All @@ -39,8 +34,6 @@ public override async Task<ApiResult> ProcessAsync()
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
};

await using var repository = DatabaseBuilder.CreateRepository();

switch (detail.Type)
{
case LogType.Info or LogType.Warning or LogType.Error:
Expand All @@ -52,26 +45,28 @@ public override async Task<ApiResult> ProcessAsync()
case LogType.OverwriteUpdated:
{
var overwriteUpdated = jsonElement.Deserialize<OverwriteUpdatedDetail>(options)!;
var role = overwriteUpdated.TargetType == PermissionTarget.Role ? await DiscordClient.FindRoleAsync(overwriteUpdated.TargetId.ToUlong()) : null;
var user = overwriteUpdated.TargetType == PermissionTarget.User ? await repository.User.FindUserByIdAsync(overwriteUpdated.TargetId.ToUlong()) : null;

detail.Data = new Data.Models.API.AuditLog.Detail.OverwriteUpdatedDetail
var detailData = new Data.Models.API.AuditLog.Detail.OverwriteUpdatedDetail
{
User = Mapper.Map<Data.Models.API.Users.User>(user),
Role = Mapper.Map<Data.Models.API.Role>(role),
Allow = overwriteUpdated.Allow,
Deny = overwriteUpdated.Deny
};

if (overwriteUpdated.TargetType is PermissionTarget.Role)
detailData.Role = await _dataResolve.GetRoleAsync(overwriteUpdated.TargetId.ToUlong());
else if (overwriteUpdated.TargetType is PermissionTarget.User)
detailData.User = await _dataResolve.GetUserAsync(overwriteUpdated.TargetId.ToUlong());

detail.Data = detailData;
break;
}
case LogType.MemberUpdated:
{
var memberUpdated = jsonElement.Deserialize<MemberUpdatedDetail>(options)!;
var user = await repository.User.FindUserByIdAsync(memberUpdated.UserId.ToUlong());

detail.Data = new Data.Models.API.AuditLog.Detail.MemberUpdatedDetail
{
User = Mapper.Map<Data.Models.API.Users.User>(user),
User = (await _dataResolve.GetUserAsync(memberUpdated.UserId.ToUlong()))!,
Flags = memberUpdated.Flags,
Nickname = memberUpdated.Nickname,
IsDeaf = memberUpdated.IsDeaf,
Expand All @@ -86,11 +81,10 @@ public override async Task<ApiResult> ProcessAsync()
case LogType.MessageDeleted:
{
var messageDeleted = jsonElement.Deserialize<MessageDeletedDetail>(options)!;
var author = await repository.User.FindUserByIdAsync(messageDeleted.AuthorId.ToUlong());

detail.Data = new Data.Models.API.AuditLog.Detail.MessageDeletedDetail
{
Author = Mapper.Map<Data.Models.API.Users.User>(author),
Author = (await _dataResolve.GetUserAsync(messageDeleted.AuthorId.ToUlong()))!,
Content = messageDeleted.Content,
Embeds = messageDeleted.Embeds,
MessageCreatedAt = messageDeleted.MessageCreatedAt.ToLocalTime()
Expand All @@ -106,17 +100,20 @@ public override async Task<ApiResult> ProcessAsync()
case LogType.JobCompleted:
{
var jobCompleted = jsonElement.Deserialize<JobExecutionDetail>(options)!;
var startUser = string.IsNullOrEmpty(jobCompleted.StartUserId) ? null : await repository.User.FindUserByIdAsync(jobCompleted.StartUserId.ToUlong());

detail.Data = new Data.Models.API.AuditLog.Detail.JobExecutionDetail
var detailData = new Data.Models.API.AuditLog.Detail.JobExecutionDetail
{
Result = jobCompleted.Result,
EndAt = jobCompleted.EndAt.ToLocalTime(),
JobName = jobCompleted.JobName,
StartAt = jobCompleted.StartAt.ToLocalTime(),
StartUser = startUser is null ? null : Mapper.Map<Data.Models.API.Users.User>(startUser),
WasError = jobCompleted.WasError
};

if (!string.IsNullOrEmpty(jobCompleted.StartUserId))
detailData.StartUser = await _dataResolve.GetUserAsync(jobCompleted.StartUserId.ToUlong());

detail.Data = detailData;
break;
}
case LogType.Api:
Expand Down
137 changes: 34 additions & 103 deletions src/GrillBot.App/Actions/Api/V1/AuditLog/GetAuditLogList.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Text.Json;
using AutoMapper;
using GrillBot.App.Helpers;
using GrillBot.Common.Extensions.Discord;
using GrillBot.App.Managers.DataResolve;
using GrillBot.Common.FileStorage;
using GrillBot.Common.Models;
using GrillBot.Core.Extensions;
Expand All @@ -12,7 +11,6 @@
using GrillBot.Core.Services.AuditLog.Models.Request.Search;
using GrillBot.Data.Models.API.AuditLog;
using GrillBot.Data.Models.API.AuditLog.Preview;
using GrillBot.Database.Services.Repository;
using Microsoft.AspNetCore.Mvc;
using File = GrillBot.Data.Models.API.AuditLog.File;
using SearchModels = GrillBot.Core.Services.AuditLog.Models.Response.Search;
Expand All @@ -21,27 +19,20 @@ namespace GrillBot.App.Actions.Api.V1.AuditLog;

public class GetAuditLogList : ApiAction
{
private GrillBotDatabaseBuilder DatabaseBuilder { get; }
private IMapper Mapper { get; }
private IAuditLogServiceClient AuditLogServiceClient { get; }
private IDiscordClient DiscordClient { get; }
private BlobManagerFactoryHelper BlobManagerFactoryHelper { get; }

private Dictionary<string, Database.Entity.User> CachedUsers { get; } = new();
private Dictionary<string, Database.Entity.Guild> CachedGuilds { get; } = new();
private Dictionary<string, Dictionary<string, Database.Entity.GuildChannel>> CachedChannels { get; } = new(); // Dictionary<GuildId, Dictionary<ChannelId, Channel>>
private readonly DataResolveManager _dataResolveManager;

private BlobManager BlobManager { get; set; } = null!;
private BlobManager LegacyBlobManager { get; set; } = null!;

public GetAuditLogList(ApiRequestContext apiContext, GrillBotDatabaseBuilder databaseBuilder, IMapper mapper, IAuditLogServiceClient auditLogServiceClient, IDiscordClient discordClient,
BlobManagerFactoryHelper blobManagerFactoryHelper) : base(apiContext)
public GetAuditLogList(ApiRequestContext apiContext, IAuditLogServiceClient auditLogServiceClient, BlobManagerFactoryHelper blobManagerFactoryHelper,
DataResolveManager dataResolveManager) : base(apiContext)
{
DatabaseBuilder = databaseBuilder;
Mapper = mapper;
AuditLogServiceClient = auditLogServiceClient;
DiscordClient = discordClient;
BlobManagerFactoryHelper = blobManagerFactoryHelper;
_dataResolveManager = dataResolveManager;
}

public override async Task<ApiResult> ProcessAsync()
Expand All @@ -61,11 +52,7 @@ public override async Task<ApiResult> ProcessAsync()
LegacyBlobManager = await BlobManagerFactoryHelper.CreateLegacyAsync();
}

await using var repository = DatabaseBuilder.CreateRepository();
var result = await PaginatedResponse<LogListItem>.CopyAndMapAsync(
response.Response!,
async entity => await MapListItemAsync(repository, entity)
);
var result = await PaginatedResponse<LogListItem>.CopyAndMapAsync(response.Response!, MapListItemAsync);

return ApiResult.Ok(result);
}
Expand Down Expand Up @@ -95,7 +82,7 @@ private static AggregateException CreateValidationExceptions(ValidationProblemDe
return new AggregateException(exceptions.ToArray());
}

private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository, SearchModels.LogListItem item)
private async Task<LogListItem> MapListItemAsync(SearchModels.LogListItem item)
{
var result = new LogListItem
{
Expand All @@ -108,32 +95,20 @@ private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository,

if (!string.IsNullOrEmpty(item.GuildId))
{
var guild = await ResolveGuildAsync(repository, item.GuildId);
if (guild is not null)
{
result.Guild = Mapper.Map<Data.Models.API.Guilds.Guild>(guild);
result.Guild = await _dataResolveManager.GetGuildAsync(item.GuildId.ToUlong());

if (!string.IsNullOrEmpty(item.ChannelId))
{
var channel = await ResolveChannelAsync(repository, item.GuildId, item.ChannelId);
if (channel is not null)
result.Channel = Mapper.Map<Data.Models.API.Channels.Channel>(channel);
}
}
if (result.Guild is not null && !string.IsNullOrEmpty(item.ChannelId))
result.Channel = await _dataResolveManager.GetChannelAsync(item.GuildId.ToUlong(), item.ChannelId.ToUlong());
}

if (!string.IsNullOrEmpty(item.UserId))
{
var user = await ResolveUserAsync(repository, item.UserId);
if (user is not null)
result.User = Mapper.Map<Data.Models.API.Users.User>(user);
}
result.User = await _dataResolveManager.GetUserAsync(item.UserId.ToUlong());

result.Preview = await MapPreviewAsync(repository, item);
result.Preview = await MapPreviewAsync(item);
return result;
}

private async Task<object?> MapPreviewAsync(GrillBotRepository repository, SearchModels.LogListItem item)
private async Task<object?> MapPreviewAsync(SearchModels.LogListItem item)
{
if (item.Preview is not JsonElement jsonElement)
return null;
Expand All @@ -143,6 +118,7 @@ private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
};

switch (item.Type)
{
case LogType.Info or LogType.Warning or LogType.Error:
Expand All @@ -156,47 +132,47 @@ private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository,
case LogType.OverwriteCreated or LogType.OverwriteDeleted:
{
var preview = jsonElement.Deserialize<SearchModels.OverwritePreview>(options)!;
var role = preview.TargetType == PermissionTarget.Role ? await DiscordClient.FindRoleAsync(preview.TargetId.ToUlong()) : null;
var user = preview.TargetType == PermissionTarget.User ? await ResolveUserAsync(repository, preview.TargetId) : null;

return new OverwritePreview
var previewData = new OverwritePreview
{
Role = Mapper.Map<Data.Models.API.Role>(role),
User = Mapper.Map<Data.Models.API.Users.User>(user),
Allow = preview.Allow,
Deny = preview.Deny
};

if (preview.TargetType is PermissionTarget.Role)
previewData.Role = await _dataResolveManager.GetRoleAsync(preview.TargetId.ToUlong());
else if (preview.TargetType is PermissionTarget.User)
previewData.User = await _dataResolveManager.GetUserAsync(preview.TargetId.ToUlong());

return previewData;
}
case LogType.OverwriteUpdated:
{
var preview = jsonElement.Deserialize<SearchModels.OverwriteUpdatedPreview>(options)!;
var role = preview.TargetType == PermissionTarget.Role ? await DiscordClient.FindRoleAsync(preview.TargetId.ToUlong()) : null;
var user = preview.TargetType == PermissionTarget.User ? await ResolveUserAsync(repository, preview.TargetId) : null;
var previewData = new OverwriteUpdatedPreview();

return new OverwriteUpdatedPreview
{
Role = Mapper.Map<Data.Models.API.Role>(role),
User = Mapper.Map<Data.Models.API.Users.User>(user),
};
if (preview.TargetType is PermissionTarget.Role)
previewData.Role = await _dataResolveManager.GetRoleAsync(preview.TargetId.ToUlong());
else if (preview.TargetType is PermissionTarget.User)
previewData.User = await _dataResolveManager.GetUserAsync(preview.TargetId.ToUlong());

return previewData;
}
case LogType.Unban:
{
var preview = jsonElement.Deserialize<SearchModels.UnbanPreview>(options)!;
var user = await ResolveUserAsync(repository, preview.UserId);

return new UnbanPreview
{
User = Mapper.Map<Data.Models.API.Users.User>(user)
User = (await _dataResolveManager.GetUserAsync(preview.UserId.ToUlong()))!
};
}
case LogType.MemberUpdated:
{
var preview = jsonElement.Deserialize<SearchModels.MemberUpdatedPreview>(options)!;
var user = await ResolveUserAsync(repository, preview.UserId);

return new MemberUpdatedPreview
{
User = Mapper.Map<Data.Models.API.Users.User>(user),
User = (await _dataResolveManager.GetUserAsync(preview.UserId.ToUlong()))!,
SelfUnverifyMinimalTimeChange = preview.SelfUnverifyMinimalTimeChange,
FlagsChanged = preview.FlagsChanged,
NicknameChanged = preview.NicknameChanged,
Expand All @@ -206,24 +182,22 @@ private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository,
case LogType.MemberRoleUpdated:
{
var preview = jsonElement.Deserialize<SearchModels.MemberRoleUpdatedPreview>(options)!;
var user = await ResolveUserAsync(repository, preview.UserId);

return new MemberRoleUpdatedPreview
{
ModifiedRoles = preview.ModifiedRoles,
User = Mapper.Map<Data.Models.API.Users.User>(user)
User = (await _dataResolveManager.GetUserAsync(preview.UserId.ToUlong()))!
};
}
case LogType.GuildUpdated:
return jsonElement.Deserialize<SearchModels.GuildUpdatedPreview>(options);
case LogType.UserLeft:
{
var preview = jsonElement.Deserialize<SearchModels.UserLeftPreview>(options)!;
var user = await ResolveUserAsync(repository, preview.UserId);

return new UserLeftPreview
{
User = Mapper.Map<Data.Models.API.Users.User>(user),
User = (await _dataResolveManager.GetUserAsync(preview.UserId.ToUlong()))!,
BanReason = preview.BanReason,
IsBan = preview.IsBan,
MemberCount = preview.MemberCount
Expand All @@ -236,11 +210,10 @@ private async Task<LogListItem> MapListItemAsync(GrillBotRepository repository,
case LogType.MessageDeleted:
{
var preview = jsonElement.Deserialize<SearchModels.MessageDeletedPreview>(options)!;
var user = await ResolveUserAsync(repository, preview.AuthorId);

return new MessageDeletedPreview
{
User = Mapper.Map<Data.Models.API.Users.User>(user),
User = (await _dataResolveManager.GetUserAsync(preview.AuthorId.ToUlong()))!,
Content = preview.Content,
Embeds = preview.Embeds,
MessageCreatedAt = preview.MessageCreatedAt.ToLocalTime()
Expand Down Expand Up @@ -277,46 +250,4 @@ private File ConvertFile(SearchModels.File file, SearchModels.LogListItem item)
Size = file.Size
};
}

private async Task<Database.Entity.User?> ResolveUserAsync(GrillBotRepository repository, string userId)
{
if (CachedUsers.TryGetValue(userId, out var user))
return user;

user = await repository.User.FindUserByIdAsync(userId.ToUlong(), disableTracking: true);
if (user is null)
return null;

CachedUsers.Add(user.Id, user);
return user;
}

private async Task<Database.Entity.Guild?> ResolveGuildAsync(GrillBotRepository repository, string guildId)
{
if (CachedGuilds.TryGetValue(guildId, out var guild))
return guild;

guild = await repository.Guild.FindGuildByIdAsync(guildId.ToUlong(), true);
if (guild is null)
return null;

CachedGuilds.Add(guild.Id, guild);
return guild;
}

private async Task<Database.Entity.GuildChannel?> ResolveChannelAsync(GrillBotRepository repository, string guildId, string channelId)
{
if (CachedChannels.TryGetValue(guildId, out var guildChannels) && guildChannels.TryGetValue(channelId, out var guildChannel))
return guildChannel;

guildChannel = await repository.Channel.FindChannelByIdAsync(channelId.ToUlong(), guildId.ToUlong(), true, includeDeleted: true);
if (guildChannel is null)
return null;

if (!CachedChannels.ContainsKey(guildId))
CachedChannels.Add(guildId, new Dictionary<string, Database.Entity.GuildChannel>());

CachedChannels[guildId].Add(channelId, guildChannel);
return guildChannel;
}
}
Loading
Loading