refactor: add .editorconfig and refactor code

This commit is contained in:
2024-12-25 05:43:30 +03:00
parent 2a33ecbf07
commit 408a95e4b3
45 changed files with 371 additions and 75 deletions
.editorconfigBackend.sln
Endpoint
Security
SqlData
Application
Cqrs
Campus
Queries
GetCampusBasicInfoList
GetCampusDetails
Discipline
Queries
GetDisciplineDetails
GetDisciplineList
Faculty
Group
Queries
GetGroupList
LectureHall
Queries
GetLectureHallList
Professor
Schedule
DependencyInjection.cs
Persistence

278
.editorconfig Normal file

@ -0,0 +1,278 @@
# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии
root = true
# Файлы C#
[*.cs]
#### Основные параметры EditorConfig ####
# Отступы и интервалы
indent_size = 4
indent_style = space
tab_width = 4
# Предпочтения для новых строк
end_of_line = crlf
insert_final_newline = false
#### Действия кода .NET ####
# Члены типа
dotnet_hide_advanced_members = false
dotnet_member_insertion_location = with_other_members_of_the_same_kind
dotnet_property_generation_behavior = prefer_throwing_properties
# Поиск символов
dotnet_search_reference_assemblies = true
#### Рекомендации по написанию кода .NET ####
# Упорядочение Using
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# Предпочтения для this. и Me.
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# Параметры использования ключевых слов языка и типов BCL
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Предпочтения для скобок
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Предпочтения модификатора
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Выражения уровень предпочтения
dotnet_prefer_system_hash_code = true
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_style_prefer_conditional_expression_over_return = true
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Предпочтения для полей
dotnet_style_readonly_field = true
# Настройки параметров
dotnet_code_quality_unused_parameters = non_public
# Параметры подавления
dotnet_remove_unnecessary_suppression_exclusions = none
# Предпочтения для новых строк
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = false
#### Рекомендации по написанию кода C# ####
# Предпочтения var
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
# Члены, заданные выражениями
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_operators = true:silent
csharp_style_expression_bodied_properties = true:silent
# Настройки соответствия шаблонов
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Настройки проверки на null
csharp_style_conditional_delegate_call = true:suggestion
# Предпочтения модификатора
csharp_prefer_static_anonymous_function = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
# Предпочтения для блоков кода
csharp_prefer_braces = when_multiline:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_namespace_declarations = file_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_prefer_top_level_statements = false:silent
# Выражения уровень предпочтения
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# предпочтения для директивы using
csharp_using_directive_placement = outside_namespace:silent
# Предпочтения для новых строк
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:silent
csharp_style_allow_embedded_statements_on_same_line_experimental = false:silent
#### Правила форматирования C# ####
# Предпочтения для новых строк
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Предпочтения для отступов
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Предпочтения для интервалов
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Предпочтения переноса
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Стили именования ####
# Правила именования
dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Спецификации символов
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Стили именования
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
[*.{cs,vb}]
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_code_quality_unused_parameters = non_public:suggestion
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = false:silent
dotnet_style_readonly_field = true:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent

@ -8,6 +8,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elements of the solution", "Elements of the solution", "{3E087889-A4A0-4A55-A07D-7D149A5BC928}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.editorconfig = .editorconfig
.env = .env
.gitattributes = .gitattributes
.gitignore = .gitignore

@ -2,7 +2,7 @@
namespace Mirea.Api.Endpoint.Common.Attributes;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
public class CacheMaxAgeAttribute : Attribute
{
public int MaxAge { get; }

@ -13,7 +13,7 @@ public class TokenAuthenticationAttribute : Attribute, IActionFilter
public void OnActionExecuting(ActionExecutingContext context)
{
var setupToken = context.HttpContext.RequestServices.GetRequiredService<ISetupToken>();
if (!context.HttpContext.Request.Cookies.TryGetValue(AuthToken, out string? tokenFromCookie))
if (!context.HttpContext.Request.Cookies.TryGetValue(AuthToken, out var tokenFromCookie))
{
context.Result = new UnauthorizedResult();
return;

@ -3,6 +3,5 @@
public interface IMaintenanceModeNotConfigureService
{
bool IsMaintenanceMode { get; }
void DisableMaintenanceMode();
}

@ -3,8 +3,6 @@
public interface IMaintenanceModeService
{
bool IsMaintenanceMode { get; }
void EnableMaintenanceMode();
void DisableMaintenanceMode();
}

@ -9,5 +9,6 @@ public static class PairPeriodTimeConverter
public static Dictionary<int, Dto.Common.PairPeriodTime> ConvertToDto(this IDictionary<int, ScheduleSettings.PairPeriodTime> pairPeriod) =>
pairPeriod.ToDictionary(kvp => kvp.Key, kvp => new Dto.Common.PairPeriodTime { Start = kvp.Value.Start, End = kvp.Value.End });
public static Dictionary<int, ScheduleSettings.PairPeriodTime> ConvertFromDto(this IDictionary<int, Dto.Common.PairPeriodTime> pairPeriod) => pairPeriod.ToDictionary(kvp => kvp.Key, kvp => new ScheduleSettings.PairPeriodTime(kvp.Value.Start, kvp.Value.End));
public static Dictionary<int, ScheduleSettings.PairPeriodTime> ConvertFromDto(this IDictionary<int, Dto.Common.PairPeriodTime> pairPeriod) =>
pairPeriod.ToDictionary(kvp => kvp.Key, kvp => new ScheduleSettings.PairPeriodTime(kvp.Value.Start, kvp.Value.End));
}

@ -9,7 +9,8 @@ namespace Mirea.Api.Endpoint.Common.Services.Security;
public class DistributedCacheService(IDistributedCache cache) : ICacheService
{
public async Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default)
public async Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null,
CancellationToken cancellationToken = default)
{
var options = new DistributedCacheEntryOptions
{

@ -9,7 +9,8 @@ namespace Mirea.Api.Endpoint.Common.Services.Security;
public class MemoryCacheService(IMemoryCache cache) : ICacheService
{
public Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default)
public Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, TimeSpan? slidingExpiration = null,
CancellationToken cancellationToken = default)
{
var options = new MemoryCacheEntryOptions
{

@ -32,7 +32,7 @@ public class ScheduleSyncService : IHostedService, IDisposable
private void OnForceSyncRequested()
{
StopAsync(default).ContinueWith(_ =>
StopAsync(CancellationToken.None).ContinueWith(_ =>
{
_cancellationTokenSource = new CancellationTokenSource();
ExecuteTask(null);
@ -41,9 +41,9 @@ public class ScheduleSyncService : IHostedService, IDisposable
private void OnUpdateIntervalRequested()
{
StopAsync(default).ContinueWith(_ =>
StopAsync(CancellationToken.None).ContinueWith(_ =>
{
StartAsync(default);
StartAsync(CancellationToken.None);
});
}
@ -109,7 +109,7 @@ public class ScheduleSyncService : IHostedService, IDisposable
public void Dispose()
{
StopAsync(default).GetAwaiter().GetResult();
StopAsync(CancellationToken.None).GetAwaiter().GetResult();
_timer?.Dispose();
ScheduleSyncManager.OnForceSyncRequested -= OnForceSyncRequested;
ScheduleSyncManager.OnUpdateIntervalRequested -= OnUpdateIntervalRequested;

@ -21,7 +21,7 @@ public static class EnvironmentConfiguration
var commentIndex = line.IndexOf('#', StringComparison.Ordinal);
string arg = line;
var arg = line;
if (commentIndex != -1)
arg = arg.Remove(commentIndex, arg.Length - commentIndex);

@ -19,12 +19,14 @@ public static class JwtConfiguration
var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty);
if (jwtDecrypt.Length != 32)
throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length);
throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. " +
"Now the size is equal is " + jwtDecrypt.Length);
var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty);
if (jwtKey.Length != 64)
throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length);
throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. " +
"Now the size is " + jwtKey.Length);
var jwtIssuer = configuration["SECURITY_JWT_ISSUER"];
var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"];

@ -23,7 +23,8 @@ public class ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) :
{
Title = "MIREA Schedule Web API",
Version = description.ApiVersion.ToString(),
Description = "This API provides a convenient interface for retrieving data stored in the database. Special attention was paid to the lightweight and easy transfer of all necessary data. Made by the Winsomnia team.",
Description = "This API provides a convenient interface for retrieving data stored in the database. " +
"Special attention was paid to the lightweight and easy transfer of all necessary data. Made by the Winsomnia team.",
Contact = new OpenApiContact { Name = "Author name", Email = "support@winsomnia.net" },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
};

@ -14,8 +14,8 @@ public class SetupTokenService : ISetupToken
var token2 = Token.Value.Span;
int result = 0;
for (int i = 0; i < Token.Value.Length; i++)
var result = 0;
for (var i = 0; i < Token.Value.Length; i++)
result |= token2[i] ^ token[i];
return result == 0;

@ -144,7 +144,7 @@ public class SetupController(
[BadRequestResponse]
public ActionResult<bool> SetPsql([FromBody] DatabaseRequest request)
{
string connectionString = $"Host={request.Server}:{request.Port};Username={request.User};Database={request.Database}";
var connectionString = $"Host={request.Server}:{request.Port};Username={request.User};Database={request.Database}";
if (request.Password != null)
connectionString += $";Password={request.Password}";
if (request.Ssl)
@ -171,7 +171,7 @@ public class SetupController(
[BadRequestResponse]
public ActionResult<bool> SetMysql([FromBody] DatabaseRequest request)
{
string connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database};";
var connectionString = $"Server={request.Server}:{request.Port};Uid={request.User};Database={request.Database};";
if (request.Password != null)
connectionString += $"Pwd={request.Password};";
if (request.Ssl)
@ -241,7 +241,7 @@ public class SetupController(
[BadRequestResponse]
public ActionResult<bool> SetRedis([FromBody] CacheRequest request)
{
string connectionString = $"{request.Server}:{request.Port},ssl=false";
var connectionString = $"{request.Server}:{request.Port},ssl=false";
if (request.Password != null)
connectionString += $",password={request.Password}";

@ -23,7 +23,8 @@ using OAuthProvider = Mirea.Api.Security.Common.Domain.OAuthProvider;
namespace Mirea.Api.Endpoint.Controllers.V1;
[ApiVersion("1.0")]
public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<GeneralConfig> generalConfig, AuthService auth, PasswordHashService passwordService, OAuthService oAuthService) : BaseController
public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<GeneralConfig> generalConfig, AuthService auth,
PasswordHashService passwordService, OAuthService oAuthService) : BaseController
{
private CookieOptionsParameters GetCookieParams() =>
new()
@ -34,8 +35,8 @@ public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<Gener
private static string GenerateHtmlResponse(string title, string message, OAuthProvider? provider, bool isError = false, string? traceId = null)
{
string messageColor = isError ? "red" : "white";
string script = "<script>setTimeout(()=>{if(window.opener){window.opener.postMessage(" +
var messageColor = isError ? "red" : "white";
var script = "<script>setTimeout(()=>{if(window.opener){window.opener.postMessage(" +
"{success:" + (!isError).ToString().ToLower() +
",provider:'" + (provider == null ? "null" : (int)provider) +
"',providerName:'" + (provider == null ? "null" : Enum.GetName(provider.Value)) +
@ -80,7 +81,8 @@ public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<Gener
if (!userEntity.OAuthProviders.TryAdd(provider, oAuthUser))
{
title = "Ошибка связи аккаунта!";
message = "Этот OAuth провайдер уже связан с вашей учетной записью. Пожалуйста, используйте другого провайдера или удалите связь с аккаунтом.";
message = "Этот OAuth провайдер уже связан с вашей учетной записью. " +
"Пожалуйста, используйте другого провайдера или удалите связь с аккаунтом.";
return Content(GenerateHtmlResponse(title, message, provider, true, traceId), "text/html");
}
@ -138,7 +140,8 @@ public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<Gener
if (!Enum.IsDefined(typeof(OAuthProvider), provider))
throw new ControllerArgumentException("There is no selected provider");
return Redirect(oAuthService.GetProviderRedirect(HttpContext, GetCookieParams(), HttpContext.GetApiUrl(Url.Action("OAuth2")!), (OAuthProvider)provider).AbsoluteUri);
return Redirect(oAuthService.GetProviderRedirect(HttpContext, GetCookieParams(), HttpContext.GetApiUrl(Url.Action("OAuth2")!),
(OAuthProvider)provider).AbsoluteUri);
}
/// <summary>

@ -24,7 +24,8 @@ public class DisciplineController(IMediator mediator) : BaseController
/// <returns>Paginated list of disciplines.</returns>
[HttpGet]
[BadRequestResponse]
public async Task<ActionResult<List<DisciplineResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page, [FromQuery][Range(1, int.MaxValue)] int? pageSize)
public async Task<ActionResult<List<DisciplineResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page,
[FromQuery][Range(1, int.MaxValue)] int? pageSize)
{
var result = await mediator.Send(new GetDisciplineListQuery()
{

@ -23,7 +23,8 @@ public class FacultyController(IMediator mediator) : BaseController
/// <returns>Paginated list of faculties.</returns>
[HttpGet]
[BadRequestResponse]
public async Task<ActionResult<List<FacultyResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page, [FromQuery][Range(1, int.MaxValue)] int? pageSize)
public async Task<ActionResult<List<FacultyResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page,
[FromQuery][Range(1, int.MaxValue)] int? pageSize)
{
var result = await mediator.Send(new GetFacultyListQuery()
{

@ -38,7 +38,8 @@ public class GroupController(IMediator mediator) : BaseController
/// <returns>A list of groups.</returns>
[HttpGet]
[BadRequestResponse]
public async Task<ActionResult<List<GroupResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page, [FromQuery][Range(1, int.MaxValue)] int? pageSize)
public async Task<ActionResult<List<GroupResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page,
[FromQuery][Range(1, int.MaxValue)] int? pageSize)
{
var result = await mediator.Send(new GetGroupListQuery()
{

@ -49,7 +49,7 @@ public class ImportController(IMediator mediator, IOptionsSnapshot<GeneralConfig
GroupIds = request.Groups,
LectureHallIds = request.LectureHalls,
ProfessorIds = request.Professors
})).Schedules;
})).Schedules.ToList();
if (result.Count == 0)
return NoContent();
@ -58,8 +58,8 @@ public class ImportController(IMediator mediator, IOptionsSnapshot<GeneralConfig
using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add("Расписание");
int row = 1;
int col = 1;
var row = 1;
var col = 1;
worksheet.Cells[row, col++].Value = "День";
worksheet.Cells[row, col++].Value = "Пара";

@ -26,7 +26,8 @@ public class ProfessorController(IMediator mediator) : BaseController
/// <returns>A list of professors.</returns>
[HttpGet]
[BadRequestResponse]
public async Task<ActionResult<List<ProfessorResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page, [FromQuery][Range(1, int.MaxValue)] int? pageSize)
public async Task<ActionResult<List<ProfessorResponse>>> Get([FromQuery][Range(0, int.MaxValue)] int? page,
[FromQuery][Range(1, int.MaxValue)] int? pageSize)
{
var result = await mediator.Send(new GetProfessorListQuery()
{

@ -66,7 +66,7 @@ public class ScheduleController(IMediator mediator, IOptionsSnapshot<GeneralConf
GroupIds = request.Groups,
LectureHallIds = request.LectureHalls,
ProfessorIds = request.Professors
})).Schedules;
})).Schedules.ToList();
if (result.Count == 0)
NoContent();

@ -19,7 +19,8 @@ using Group = Mirea.Api.DataAccess.Domain.Schedule.Group;
namespace Mirea.Api.Endpoint.Sync;
internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSnapshot<GeneralConfig> config, ILogger<ScheduleSynchronizer> logger, IMaintenanceModeService maintenanceMode)
internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSnapshot<GeneralConfig> config, ILogger<ScheduleSynchronizer> logger,
IMaintenanceModeService maintenanceMode)
{
private readonly DataRepository<Campus> _campuses = new([.. dbContext.Campuses]);
private readonly DataRepository<Discipline> _disciplines = new([.. dbContext.Disciplines]);
@ -120,7 +121,7 @@ internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSna
{
hall = [];
campuses = [];
for (int i = 0; i < groupInfo.Campuses.Length; i++)
for (var i = 0; i < groupInfo.Campuses.Length; i++)
{
var campus = groupInfo.Campuses[i];
campuses.Add(_campuses.GetOrCreate(
@ -151,7 +152,7 @@ internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSna
Name = groupInfo.Discipline
});
var lesson = _lessons.GetOrCreate(l =>
Lesson lesson = _lessons.GetOrCreate(l =>
l.IsEven == groupInfo.IsEven &&
l.DayOfWeek == groupInfo.Day &&
l.PairNumber == groupInfo.Pair &&
@ -182,9 +183,9 @@ internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSna
return lesson;
});
int maxValue = int.Max(int.Max(professor?.Count ?? -1, hall?.Count ?? -1), 1);
var maxValue = int.Max(int.Max(professor?.Count ?? -1, hall?.Count ?? -1), 1);
for (int i = 0; i < maxValue; i++)
for (var i = 0; i < maxValue; i++)
{
var prof = professor?.ElementAtOrDefault(i);
var lectureHall = hall?.ElementAtOrDefault(i);
@ -226,7 +227,9 @@ internal partial class ScheduleSynchronizer(UberDbContext dbContext, IOptionsSna
if (pairPeriods == null || startTerm == null)
{
logger.LogWarning("It is not possible to synchronize the schedule due to the fact that the {Arg1} or {Arg2} variable is not initialized.", nameof(pairPeriods), nameof(startTerm));
logger.LogWarning("It is not possible to synchronize the schedule due to the fact that the {Arg1} or {Arg2} variable is not initialized.",
nameof(pairPeriods),
nameof(startTerm));
return;
}

@ -2,7 +2,7 @@
namespace Mirea.Api.Security.Common.Domain.OAuth2;
internal struct OAuthProviderUrisData
internal readonly struct OAuthProviderUrisData
{
public string RedirectUrl { get; init; }
public string TokenUrl { get; init; }

@ -61,7 +61,8 @@ public static class DependencyInjection
providers.Add(provider, (clientId, secret));
}
services.AddSingleton(provider => new OAuthService(provider.GetRequiredService<ILogger<OAuthService>>(), providers, configuration["SECURITY_ENCRYPTION_TOKEN"]!));
services.AddSingleton(provider => new OAuthService(provider.GetRequiredService<ILogger<OAuthService>>(), providers,
configuration["SECURITY_ENCRYPTION_TOKEN"]!));
return services;
}

@ -12,7 +12,7 @@ public static class GeneratorKey
var random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
string charsForGenerate = excludes?
var charsForGenerate = excludes?
.Aggregate(chars, (current, ex) => current.Replace(ex.ToString(), string.Empty)) ?? chars;
if (!string.IsNullOrEmpty(includes))

@ -51,7 +51,8 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
}
};
private static async Task<OAuthTokenResponse?> ExchangeCodeForTokensAsync(string requestUri, string redirectUrl, string code, string clientId, string secret, CancellationToken cancellation)
private static async Task<OAuthTokenResponse?> ExchangeCodeForTokensAsync(string requestUri, string redirectUrl, string code,
string clientId, string secret, CancellationToken cancellation)
{
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, requestUri)
{
@ -77,7 +78,8 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
return JsonSerializer.Deserialize<OAuthTokenResponse>(data);
}
private static async Task<OAuthUser?> GetUserProfileAsync(string requestUri, string authHeader, string accessToken, OAuthProvider provider, CancellationToken cancellation)
private static async Task<OAuthUser?> GetUserProfileAsync(string requestUri, string authHeader, string accessToken, OAuthProvider provider,
CancellationToken cancellation)
{
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
@ -130,7 +132,8 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
public (OAuthProvider Provider, Uri Redirect)[] GetAvailableProviders(string redirectUri) =>
[.. providers.Select(x => (x.Key, new Uri(redirectUri.TrimEnd('/') + "/?provider=" + (int)x.Key)))];
public async Task<(OAuthProvider provider, OAuthUser User)> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions, string redirectUrl, string code, string state, CancellationToken cancellation = default)
public async Task<(OAuthProvider provider, OAuthUser User)> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions,
string redirectUrl, string code, string state, CancellationToken cancellation = default)
{
var partsState = state.Split('_');
@ -160,11 +163,14 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
OAuthTokenResponse? accessToken = null;
try
{
accessToken = await ExchangeCodeForTokensAsync(currentProviderStruct.TokenUrl, redirectUrl, code, providerInfo.ClientId, providerInfo.Secret, cancellation);
accessToken = await ExchangeCodeForTokensAsync(currentProviderStruct.TokenUrl, redirectUrl, code, providerInfo.ClientId,
providerInfo.Secret, cancellation);
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to exchange code for access token with provider {Provider}. State: {State}", provider, state);
logger.LogWarning(ex, "Failed to exchange code for access token with provider {Provider}. State: {State}",
provider,
secretStateData);
}
if (accessToken == null)
@ -173,7 +179,8 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
OAuthUser? result = null;
try
{
result = await GetUserProfileAsync(currentProviderStruct.UserInfoUrl, currentProviderStruct.AuthHeader, accessToken.AccessToken, provider, cancellation);
result = await GetUserProfileAsync(currentProviderStruct.UserInfoUrl, currentProviderStruct.AuthHeader, accessToken.AccessToken,
provider, cancellation);
}
catch (Exception ex)
{

@ -34,8 +34,8 @@ public class PasswordHashService
if (a.Length != b.Length)
return false;
int result = 0;
for (int i = 0; i < a.Length; i++)
var result = 0;
for (var i = 0; i < a.Length; i++)
result |= a[i] ^ b[i];
return result == 0;
}

@ -10,5 +10,5 @@ public class CampusBasicInfoVm
/// <summary>
/// The list of campus basic information.
/// </summary>
public IList<CampusBasicInfoDto> Campuses { get; set; } = new List<CampusBasicInfoDto>();
public IEnumerable<CampusBasicInfoDto> Campuses { get; set; } = [];
}

@ -11,7 +11,8 @@ public class GetCampusDetailsQueryHandler(ICampusDbContext dbContext) : IRequest
{
public async Task<CampusDetailsVm> Handle(GetCampusDetailsQuery request, CancellationToken cancellationToken)
{
var campus = await dbContext.Campuses.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken) ?? throw new NotFoundException(typeof(Domain.Schedule.Campus), request.Id);
var campus = await dbContext.Campuses.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken) ??
throw new NotFoundException(typeof(Domain.Schedule.Campus), request.Id);
return new CampusDetailsVm()
{

@ -11,7 +11,8 @@ public class GetDisciplineInfoQueryHandler(IDisciplineDbContext dbContext) : IRe
{
public async Task<DisciplineInfoVm> Handle(GetDisciplineInfoQuery request, CancellationToken cancellationToken)
{
var discipline = await dbContext.Disciplines.FirstOrDefaultAsync(d => d.Id == request.Id, cancellationToken) ?? throw new NotFoundException(typeof(Domain.Schedule.Discipline), request.Id);
var discipline = await dbContext.Disciplines.FirstOrDefaultAsync(d => d.Id == request.Id, cancellationToken) ??
throw new NotFoundException(typeof(Domain.Schedule.Discipline), request.Id);
return new DisciplineInfoVm()
{

@ -10,5 +10,5 @@ public class DisciplineListVm
/// <summary>
/// The list of disciplines.
/// </summary>
public IList<DisciplineLookupDto> Disciplines { get; set; } = new List<DisciplineLookupDto>();
public IEnumerable<DisciplineLookupDto> Disciplines { get; set; } = [];
}

@ -10,5 +10,5 @@ public class FacultyListVm
/// <summary>
/// The list of faculties.
/// </summary>
public IList<FacultyLookupDto> Faculties { get; set; } = new List<FacultyLookupDto>();
public IEnumerable<FacultyLookupDto> Faculties { get; set; } = [];
}

@ -14,9 +14,4 @@ public class FacultyLookupDto
/// The name of the faculty.
/// </summary>
public required string Name { get; set; }
/// <summary>
/// ID indicating the faculty's affiliation to the campus.
/// </summary>
public int? CampusId { get; set; }
}

@ -10,5 +10,5 @@ public class GroupListVm
/// <summary>
/// The list of groups.
/// </summary>
public IList<GroupLookupDto> Groups { get; set; } = new List<GroupLookupDto>();
public IEnumerable<GroupLookupDto> Groups { get; set; } = [];
}

@ -10,5 +10,5 @@ public class LectureHallListVm
/// <summary>
/// The list of lecture hall.
/// </summary>
public IList<LectureHallLookupDto> LectureHalls { get; set; } = new List<LectureHallLookupDto>();
public IEnumerable<LectureHallLookupDto> LectureHalls { get; set; } = [];
}

@ -11,7 +11,8 @@ public class GetProfessorInfoQueryHandler(IProfessorDbContext dbContext) : IRequ
{
public async Task<ProfessorInfoVm> Handle(GetProfessorInfoQuery request, CancellationToken cancellationToken)
{
var professor = await dbContext.Professors.FirstOrDefaultAsync(p => p.Id == request.Id, cancellationToken) ?? throw new NotFoundException(typeof(Domain.Schedule.Professor), request.Id);
var professor = await dbContext.Professors.FirstOrDefaultAsync(p => p.Id == request.Id, cancellationToken) ??
throw new NotFoundException(typeof(Domain.Schedule.Professor), request.Id);
return new ProfessorInfoVm()
{

@ -27,7 +27,7 @@ public class GetProfessorInfoSearchQueryHandler(IProfessorDbContext dbContext) :
Id = x.Id,
Name = x.Name,
AltName = x.AltName
}).ToList()
})
};
}
}

@ -11,5 +11,5 @@ public class ProfessorInfoListVm
/// <summary>
/// List of <see cref="ProfessorInfoVm"/>
/// </summary>
public required IList<ProfessorInfoVm> Details { get; set; }
public IEnumerable<ProfessorInfoVm> Details { get; set; } = [];
}

@ -10,5 +10,5 @@ public class ProfessorListVm
/// <summary>
/// The list of professors.
/// </summary>
public IList<ProfessorLookupDto> Professors { get; set; } = new List<ProfessorLookupDto>();
public IEnumerable<ProfessorLookupDto> Professors { get; set; } = [];
}

@ -83,7 +83,7 @@ public class GetScheduleListQueryHandler(ILessonDbContext dbContext) : IRequestH
.Select(la => la.ProfessorId),
LinkToMeet = l.LessonAssociations!.Select(la => la.LinkToMeet)
}).ToList();
});
return new ScheduleListVm
{

@ -10,5 +10,5 @@ public class ScheduleListVm
/// <summary>
/// Gets or sets the list of schedules.
/// </summary>
public IList<ScheduleLookupDto> Schedules { get; set; } = new List<ScheduleLookupDto>();
public IEnumerable<ScheduleLookupDto> Schedules { get; set; } = [];
}

@ -1,4 +1,4 @@
using FluentValidation;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Mirea.Api.DataAccess.Application.Common.Behaviors;
@ -11,7 +11,7 @@ public static class DependencyInjection
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
services.AddValidatorsFromAssemblies(new[] { Assembly.GetExecutingAssembly() });
services.AddValidatorsFromAssemblies([Assembly.GetExecutingAssembly()]);
services.AddTransient(typeof(IPipelineBehavior<,>),
typeof(ValidationBehavior<,>));
return services;

@ -9,7 +9,7 @@ public static class ModelBuilderExtensions
{
var applyGenericMethod = typeof(ModelBuilder)
.GetMethods()
.First(m => m.Name == "ApplyConfiguration" &&
.First(m => m.Name == nameof(ApplyConfiguration) &&
m.GetParameters().Any(p => p.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)));
var entityType = configuration.GetType().GetInterfaces()

@ -4,8 +4,6 @@ namespace Mirea.Api.DataAccess.Persistence;
public static class DbInitializer
{
public static void Initialize(DbContext dbContext)
{
public static void Initialize(DbContext dbContext) =>
dbContext.Database.Migrate();
}
}