refactor(http): удалить интерфейс IHttpClient и упростить реализацию AppHttpClient (#42)

* refactor(http): удалить интерфейс IHttpClient и упростить реализацию AppHttpClient

- Удален интерфейс IHttpClient, что упростило структуру кода.
- AppHttpClient теперь напрямую использует Dio без промежуточного интерфейса.
- Обновлены зависимости в репозиториях для использования нового HTTP клиента.

* refactor(code):  dart format

* chore(pr-template): удалить отключения markdownlint из шаблона PR

* docs(copilot-instructions): добавить правила проведения Code Review

---------

Co-authored-by: petrovyuri <petrovyuri@example.com>
This commit is contained in:
Yuri Petrov
2025-12-11 10:27:19 +03:00
committed by GitHub
parent ab64fb9246
commit 4a49083ef3
15 changed files with 54 additions and 258 deletions

View File

@@ -6,6 +6,11 @@
--- ---
# Правила проведения Code Review
## Основные правила проведения Code Review
- Комментарии и описание Pull Request должны быть на русском языке.
# Стиль кода # Стиль кода
## Именование ## Именование

View File

@@ -1,5 +1,3 @@
<!-- markdownlint-disable MD033 -->
<!-- markdownlint-disable MD041 -->
## Ссылка на задачу или issue (обязательно) ## Ссылка на задачу или issue (обязательно)
<!--- https://tracker.yandex.ru/XXX --> <!--- https://tracker.yandex.ru/XXX -->

View File

@@ -1,13 +1,12 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:friflex_starter/app/app_config/app_config.dart'; import 'package:friflex_starter/app/app_config/app_config.dart';
import 'package:friflex_starter/app/http/i_http_client.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart'; import 'package:friflex_starter/features/debug/i_debug_service.dart';
/// {@template app_http_client} /// {@template app_http_client}
/// Класс для реализации HTTP-клиента для управления запросами /// Класс для реализации HTTP-клиента для управления запросами
/// {@endtemplate} /// {@endtemplate}
final class AppHttpClient implements IHttpClient { final class AppHttpClient {
/// Создает HTTP клиент /// Создает HTTP клиент
/// ///
/// Принимает: /// Принимает:
@@ -18,7 +17,6 @@ final class AppHttpClient implements IHttpClient {
required IAppConfig appConfig, required IAppConfig appConfig,
}) { }) {
_httpClient = Dio(); _httpClient = Dio();
_appConfig = appConfig;
_httpClient.options _httpClient.options
..baseUrl = appConfig.baseUrl ..baseUrl = appConfig.baseUrl
@@ -30,111 +28,8 @@ final class AppHttpClient implements IHttpClient {
_httpClient.interceptors.add(debugService.dioLogger); _httpClient.interceptors.add(debugService.dioLogger);
} }
/// Конфигурация приложения
late final IAppConfig _appConfig;
/// Экземпляр HTTP клиента /// Экземпляр HTTP клиента
late final Dio _httpClient; late final Dio _httpClient;
@override Dio get client => _httpClient;
Future<Response> get(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.get(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
@override
Future<Response> post(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.post(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
@override
Future<Response> patch(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.patch(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
@override
Future<Response> put(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.put(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
@override
Future<Response> delete(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.delete(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
@override
Future<Response> head(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
}) async {
_httpClient.options.baseUrl = _appConfig.baseUrl;
return _httpClient.head(
path,
data: data,
queryParameters: queryParameters,
options: options,
);
}
} }

View File

@@ -1,94 +0,0 @@
import 'package:dio/dio.dart';
/// Класс для описания интерфейса сервиса по управлению HTTP запросами
abstract interface class IHttpClient {
/// Описывает поля HTTP клиента
const IHttpClient();
/// Наименование сервиса
static const name = 'IHttpClient';
/// Метод для реализации запроса GET
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> get(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
/// Метод для реализации запроса POST
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> post(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
/// Метод для реализации запроса PATCH
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> patch(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
/// Метод для реализации запроса PUT
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> put(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
/// Метод для реализации запроса DELETE
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> delete(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
/// Метод для реализации запроса POST
///
/// Принимает:
/// - [path] - путь к ресурсу
/// - [data] - тело запроса
/// - [queryParameters] - параметры запроса
/// - [options] - конфигурация запроса
Future<Response> head(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
});
}

View File

@@ -55,7 +55,7 @@ class AppColors extends ThemeExtension<AppColors> with _$AppColorsTailorMixin {
); );
/// Цвета тёмной темы /// Цвета тёмной темы
static const AppColors dark = AppColors( static const AppColors dark = AppColors(
testColor: Colors.green, testColor: Colors.green,
errorSnackbarBackground: Color(0xFF638B8B), errorSnackbarBackground: Color(0xFF638B8B),
successSnackbarBackground: Color(0xFF93C499), successSnackbarBackground: Color(0xFF93C499),

View File

@@ -290,21 +290,9 @@ class _Icon extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return switch (type) { return switch (type) {
.success => const Icon( .success => const Icon(Icons.check_circle, color: Colors.white, size: 32),
Icons.check_circle, .error => const Icon(Icons.error, color: Colors.white, size: 32),
color: Colors.white, .info => const Icon(Icons.info, color: Colors.white, size: 32),
size: 32,
),
.error => const Icon(
Icons.error,
color: Colors.white,
size: 32,
),
.info => const Icon(
Icons.info,
color: Colors.white,
size: 32,
),
}; };
} }
} }

View File

@@ -1,7 +1,6 @@
import 'package:friflex_starter/app/app_config/app_config.dart'; import 'package:friflex_starter/app/app_config/app_config.dart';
import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/app/http/app_http_client.dart'; import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/app/http/i_http_client.dart';
import 'package:friflex_starter/di/di_repositories.dart'; import 'package:friflex_starter/di/di_repositories.dart';
import 'package:friflex_starter/di/di_services.dart'; import 'package:friflex_starter/di/di_services.dart';
import 'package:friflex_starter/di/di_typedefs.dart'; import 'package:friflex_starter/di/di_typedefs.dart';
@@ -25,7 +24,7 @@ final class DiContainer {
late final IAppConfig appConfig; late final IAppConfig appConfig;
/// Сервис для работы с HTTP запросами /// Сервис для работы с HTTP запросами
late final IHttpClient Function(IDebugService, IAppConfig) httpClientFactory; late final AppHttpClient httpClient;
/// Репозитории приложения /// Репозитории приложения
late final DiRepositories repositories; late final DiRepositories repositories;
@@ -47,8 +46,10 @@ final class DiContainer {
}; };
// Инициализация HTTP клиента // Инициализация HTTP клиента
httpClientFactory = (debugService, appConfig) => httpClient = AppHttpClient(
AppHttpClient(debugService: debugService, appConfig: appConfig); debugService: debugService,
appConfig: appConfig,
);
// Инициализация сервисов // Инициализация сервисов
services = DiServices() services = DiServices()

View File

@@ -77,8 +77,8 @@ final class DiRepositories {
// Инициализация репозитория обновлений // Инициализация репозитория обновлений
updatesRepository = _lazyInitRepo<IUpdateRepository>( updatesRepository = _lazyInitRepo<IUpdateRepository>(
mockFactory: UpdateMockRepository.new, mockFactory: () => const UpdateMockRepository(),
mainFactory: UpdateRepository.new, mainFactory: () => UpdateRepository(httpClient: diContainer.httpClient),
onProgress: onProgress, onProgress: onProgress,
onError: onError, onError: onError,
environment: diContainer.env, environment: diContainer.env,
@@ -86,13 +86,8 @@ final class DiRepositories {
// Инициализация репозитория авторизации // Инициализация репозитория авторизации
authRepository = _lazyInitRepo<IAuthRepository>( authRepository = _lazyInitRepo<IAuthRepository>(
mockFactory: AuthMockRepository.new, mockFactory: () => const AuthMockRepository(),
mainFactory: () => AuthRepository( mainFactory: () => AuthRepository(httpClient: diContainer.httpClient),
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
),
),
onProgress: onProgress, onProgress: onProgress,
onError: onError, onError: onError,
environment: diContainer.env, environment: diContainer.env,
@@ -100,13 +95,8 @@ final class DiRepositories {
// Инициализация репозитория сервиса управления токеном доступа // Инициализация репозитория сервиса управления токеном доступа
mainRepository = _lazyInitRepo<IMainRepository>( mainRepository = _lazyInitRepo<IMainRepository>(
mockFactory: MainMockRepository.new, mockFactory: () => const MainMockRepository(),
mainFactory: () => MainRepository( mainFactory: () => MainRepository(httpClient: diContainer.httpClient),
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
),
),
onProgress: onProgress, onProgress: onProgress,
onError: onError, onError: onError,
environment: diContainer.env, environment: diContainer.env,
@@ -114,13 +104,8 @@ final class DiRepositories {
// Инициализация репозитория профиля // Инициализация репозитория профиля
profileRepository = _lazyInitRepo<IProfileRepository>( profileRepository = _lazyInitRepo<IProfileRepository>(
mockFactory: ProfileMockRepository.new, mockFactory: () => const ProfileMockRepository(),
mainFactory: () => ProfileRepository( mainFactory: () => ProfileRepository(httpClient: diContainer.httpClient),
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
),
),
onProgress: onProgress, onProgress: onProgress,
onError: onError, onError: onError,
environment: diContainer.env, environment: diContainer.env,

View File

@@ -4,6 +4,9 @@ import 'package:friflex_starter/features/auth/domain/repository/i_auth_repositor
/// Mock реализация репозитория авторизации /// Mock реализация репозитория авторизации
/// {@endtemplate} /// {@endtemplate}
final class AuthMockRepository implements IAuthRepository { final class AuthMockRepository implements IAuthRepository {
/// {@macro AuthMockRepository}
const AuthMockRepository();
@override @override
String get name => 'AuthMockRepository'; String get name => 'AuthMockRepository';
} }

View File

@@ -1,4 +1,4 @@
import 'package:friflex_starter/app/http/i_http_client.dart'; import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/features/auth/domain/repository/i_auth_repository.dart'; import 'package:friflex_starter/features/auth/domain/repository/i_auth_repository.dart';
@@ -7,7 +7,9 @@ import 'package:friflex_starter/features/auth/domain/repository/i_auth_repositor
/// {@endtemplate} /// {@endtemplate}
final class AuthRepository implements IAuthRepository { final class AuthRepository implements IAuthRepository {
AuthRepository({required this.httpClient}); AuthRepository({required this.httpClient});
final IHttpClient httpClient;
/// Экземпляр HTTP клиента для взаимодействия с сервером
final AppHttpClient httpClient;
@override @override
String get name => 'AuthRepository'; String get name => 'AuthRepository';

View File

@@ -1,9 +1,12 @@
import 'package:friflex_starter/features/main/domain/repository/i_main_repository.dart'; import 'package:friflex_starter/features/main/domain/repository/i_main_repository.dart';
/// {@template MainMockRepository} /// {@template MainMockRepository}
/// /// Мок реализация репозитория главного сервиса
/// {@endtemplate} /// {@endtemplate}
final class MainMockRepository implements IMainRepository { final class MainMockRepository implements IMainRepository {
/// {@macro MainMockRepository}
const MainMockRepository();
@override @override
String get name => 'MainMockRepository'; String get name => 'MainMockRepository';
} }

View File

@@ -1,13 +1,14 @@
import 'package:friflex_starter/app/http/i_http_client.dart'; import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/features/main/domain/repository/i_main_repository.dart'; import 'package:friflex_starter/features/main/domain/repository/i_main_repository.dart';
/// {@template MainRepository} /// {@template MainRepository}
/// /// Реализация репозитория главного сервиса
/// {@endtemplate} /// {@endtemplate}
final class MainRepository implements IMainRepository { final class MainRepository implements IMainRepository {
MainRepository({required this.httpClient}); MainRepository({required this.httpClient});
final IHttpClient httpClient;
/// Экземпляр HTTP клиента для взаимодействия с сервером
final AppHttpClient httpClient;
@override @override
String get name => 'MainRepository'; String get name => 'MainRepository';

View File

@@ -1,9 +1,12 @@
import 'package:friflex_starter/features/profile/domain/repository/i_profile_repository.dart'; import 'package:friflex_starter/features/profile/domain/repository/i_profile_repository.dart';
/// {@template ProfileMockRepository} /// {@template ProfileMockRepository}
/// /// Мок реализация репозитория профиля пользователя
/// {@endtemplate} /// {@endtemplate}
final class ProfileMockRepository implements IProfileRepository { final class ProfileMockRepository implements IProfileRepository {
/// {@macro ProfileMockRepository}
const ProfileMockRepository();
@override @override
String get name => 'ProfileMockRepository'; String get name => 'ProfileMockRepository';

View File

@@ -1,13 +1,15 @@
import 'package:friflex_starter/app/http/i_http_client.dart'; import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/features/profile/domain/repository/i_profile_repository.dart'; import 'package:friflex_starter/features/profile/domain/repository/i_profile_repository.dart';
/// {@template ProfileRepository} /// {@template ProfileRepository}
/// /// Реализация репозитория профиля пользователя
/// {@endtemplate} /// {@endtemplate}
final class ProfileRepository implements IProfileRepository { final class ProfileRepository implements IProfileRepository {
ProfileRepository({required this.httpClient}); ProfileRepository({required this.httpClient});
final IHttpClient httpClient;
/// Экземпляр HTTP клиента для взаимодействия с сервером
final AppHttpClient httpClient;
@override @override
String get name => 'ProfileRepository'; String get name => 'ProfileRepository';

View File

@@ -1,3 +1,4 @@
import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart'; import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart'; import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart';
@@ -6,7 +7,10 @@ import 'package:friflex_starter/features/update/domain/repository/i_update_repos
/// {@endtemplate} /// {@endtemplate}
final class UpdateRepository implements IUpdateRepository { final class UpdateRepository implements IUpdateRepository {
/// {@macro UpdateRepository} /// {@macro UpdateRepository}
const UpdateRepository(); UpdateRepository({required this.httpClient});
/// Экземпляр HTTP клиента для взаимодействия с сервером
final AppHttpClient httpClient;
@override @override
Future<UpdateEntity> checkForUpdates({ Future<UpdateEntity> checkForUpdates({