enhancement: 测试

This commit is contained in:
rang 2025-12-23 17:54:46 +08:00
parent 6efa41e035
commit 7d3e22afb7
9 changed files with 95 additions and 48 deletions

3
.vscode/launch.json vendored
View file

@ -8,8 +8,7 @@
"name": "vaani", "name": "vaani",
"request": "launch", "request": "launch",
"program": "lib/main.dart", "program": "lib/main.dart",
"type": "dart", "type": "dart"
"console": "terminal"
}, },
{ {
"name": "vaani (profile mode)", "name": "vaani (profile mode)",

View file

@ -3,26 +3,26 @@
import 'dart:convert'; import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart'; // import 'package:http_cache_client/http_cache_client.dart';
// import 'package:http_cache_core/http_cache_core.dart'; // import 'package:http_cache_core/http_cache_core.dart';
// import 'package:http_cache_isar_store/http_cache_isar_store.dart'; // import 'package:http_cache_hive_store/http_cache_hive_store.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/db/cache/cache_key.dart'; import 'package:vaani/db/cache/cache_key.dart';
import 'package:vaani/db/cache_manager.dart'; import 'package:vaani/db/cache_manager.dart';
import 'package:vaani/shared/utils/error_response.dart';
import 'package:vaani/features/settings/api_settings_provider.dart'; import 'package:vaani/features/settings/api_settings_provider.dart';
import 'package:vaani/features/settings/models/authenticated_user.dart'; import 'package:vaani/features/settings/models/authenticated_user.dart';
import 'package:vaani/shared/extensions/obfuscation.dart'; import 'package:vaani/shared/extensions/obfuscation.dart';
import 'package:vaani/shared/utils/error_response.dart';
part 'api_provider.g.dart'; part 'api_provider.g.dart';
// TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718 // // TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718
typedef ResponseErrorHandler = void Function( // typedef ResponseErrorHandler<T> = void Function(
Response response, [ // T response, [
Object? error, // Object? error,
]); // ]);
final _logger = Logger('api_provider'); final _logger = Logger('api_provider');
@ -39,15 +39,17 @@ Uri makeBaseUrl(String address) {
// Global options // Global options
// final options = CacheOptions( // final options = CacheOptions(
// // A default store is required for the client. // // A default store is required for the client.
// store: IsarCacheStore("", name: "http_cache"), // store: HiveCacheStore(appDocumentsDir.path, hiveBoxName: "http_cache"),
// // All subsequent fields are optional to get a standard behaviour. // // All subsequent fields are optional to get a standard behaviour.
// // Default. // // Default.
// policy: CachePolicy.request, // policy: CachePolicy.request,
// //
// // Returns a previous cached response on error for given status codes. // // Returns a previous cached response on error for given status codes.
// // Defaults to `[]`. // // Defaults to `[]`.
// hitCacheOnErrorCodes: const [500], // hitCacheOnErrorCodes: const [500],
// // 使
// // Allows to return a cached response on network errors (e.g. offline usage). // // Allows to return a cached response on network errors (e.g. offline usage).
// // Defaults to `false`. // // Defaults to `false`.
// hitCacheOnNetworkFailure: true, // hitCacheOnNetworkFailure: true,
@ -72,9 +74,8 @@ AudiobookshelfApi audiobookshelfApi(Ref ref, Uri? baseUrl) {
// try to get the base url from app settings // try to get the base url from app settings
final apiSettings = ref.watch(apiSettingsProvider); final apiSettings = ref.watch(apiSettingsProvider);
baseUrl ??= apiSettings.activeServer?.serverUrl; baseUrl ??= apiSettings.activeServer?.serverUrl;
return AudiobookshelfApi( return DioAudiobookshelfApi(
baseUrl: makeBaseUrl(baseUrl.toString()), baseUrl: makeBaseUrl(baseUrl.toString()),
// client:
); );
} }
@ -88,9 +89,10 @@ AudiobookshelfApi authenticatedApi(Ref ref) {
_logger.severe('No active user can not provide authenticated api'); _logger.severe('No active user can not provide authenticated api');
throw StateError('No active user'); throw StateError('No active user');
} }
return AudiobookshelfApi( return DioAudiobookshelfApi(
baseUrl: makeBaseUrl(user.server.serverUrl.toString()), baseUrl: makeBaseUrl(user.server.serverUrl.toString()),
token: user.authToken, token: user.authToken,
// client: CacheClient(Client(), options: options),
); );
} }
@ -102,7 +104,7 @@ FutureOr<bool> isServerAlive(Ref ref, String address) async {
} }
try { try {
return await AudiobookshelfApi(baseUrl: makeBaseUrl(address)) return await DioAudiobookshelfApi(baseUrl: makeBaseUrl(address))
.server .server
.ping() ?? .ping() ??
false; false;

View file

@ -6,7 +6,7 @@ part of 'api_provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$audiobookshelfApiHash() => r'f23a06c404e11867a7f796877eaca99b8ff25458'; String _$audiobookshelfApiHash() => r'd7fbddf9ce2b463468c8d4db5a1bc4a53b7b7278';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@ -170,7 +170,7 @@ class _AudiobookshelfApiProviderElement
Uri? get baseUrl => (origin as AudiobookshelfApiProvider).baseUrl; Uri? get baseUrl => (origin as AudiobookshelfApiProvider).baseUrl;
} }
String _$authenticatedApiHash() => r'284be2c39823c20fb70035a136c430862c28fa27'; String _$authenticatedApiHash() => r'13bba42fa712f173d3b72761ae9d544854df26d0';
/// get the api instance for the authenticated user /// get the api instance for the authenticated user
/// ///
@ -191,7 +191,7 @@ final authenticatedApiProvider = Provider<AudiobookshelfApi>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef AuthenticatedApiRef = ProviderRef<AudiobookshelfApi>; typedef AuthenticatedApiRef = ProviderRef<AudiobookshelfApi>;
String _$isServerAliveHash() => r'bb3a53cae1eb64b8760a56864feed47b7a3f1c29'; String _$isServerAliveHash() => r'3afd608ced03a23fa7300d4a59368d170406ecc8';
/// ping the server to check if it is reachable /// ping the server to check if it is reachable
/// ///
@ -355,7 +355,7 @@ class ServerStatusFamily extends Family<AsyncValue<ServerStatusResponse?>> {
/// Copied from [serverStatus]. /// Copied from [serverStatus].
ServerStatusProvider call( ServerStatusProvider call(
Uri baseUrl, [ Uri baseUrl, [
void Function(Response, [Object?])? responseErrorHandler, void Function(ApiResponse, [Object?])? responseErrorHandler,
]) { ]) {
return ServerStatusProvider( return ServerStatusProvider(
baseUrl, baseUrl,
@ -398,7 +398,7 @@ class ServerStatusProvider
/// Copied from [serverStatus]. /// Copied from [serverStatus].
ServerStatusProvider( ServerStatusProvider(
Uri baseUrl, [ Uri baseUrl, [
void Function(Response, [Object?])? responseErrorHandler, void Function(ApiResponse, [Object?])? responseErrorHandler,
]) : this._internal( ]) : this._internal(
(ref) => serverStatus( (ref) => serverStatus(
ref as ServerStatusRef, ref as ServerStatusRef,
@ -430,7 +430,7 @@ class ServerStatusProvider
}) : super.internal(); }) : super.internal();
final Uri baseUrl; final Uri baseUrl;
final void Function(Response, [Object?])? responseErrorHandler; final void Function(ApiResponse, [Object?])? responseErrorHandler;
@override @override
Override overrideWith( Override overrideWith(
@ -480,7 +480,7 @@ mixin ServerStatusRef on AutoDisposeFutureProviderRef<ServerStatusResponse?> {
Uri get baseUrl; Uri get baseUrl;
/// The parameter `responseErrorHandler` of this provider. /// The parameter `responseErrorHandler` of this provider.
void Function(Response, [Object?])? get responseErrorHandler; void Function(ApiResponse, [Object?])? get responseErrorHandler;
} }
class _ServerStatusProviderElement class _ServerStatusProviderElement
@ -491,7 +491,7 @@ class _ServerStatusProviderElement
@override @override
Uri get baseUrl => (origin as ServerStatusProvider).baseUrl; Uri get baseUrl => (origin as ServerStatusProvider).baseUrl;
@override @override
void Function(Response, [Object?])? get responseErrorHandler => void Function(ApiResponse, [Object?])? get responseErrorHandler =>
(origin as ServerStatusProvider).responseErrorHandler; (origin as ServerStatusProvider).responseErrorHandler;
} }

View file

@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/features/player/core/abs_audio_player.dart'; import 'package:vaani/features/player/core/abs_audio_player.dart';
import 'package:vaani/shared/extensions/obfuscation.dart'; import 'package:vaani/shared/extensions/obfuscation.dart';
import 'package:vaani/shared/utils/error_response.dart';
final _logger = Logger('PlaybackReporter'); final _logger = Logger('PlaybackReporter');
@ -265,7 +265,7 @@ class PlaybackReporter {
_logger.fine('cancelled timer'); _logger.fine('cancelled timer');
} }
void _responseErrorHandler(http.Response response, [error]) { void _responseErrorHandler(ApiResponse response, [error]) {
if (response.statusCode != 200) { if (response.statusCode != 200) {
_logger.severe('Error with api: ${response.obfuscate()}, $error'); _logger.severe('Error with api: ${response.obfuscate()}, $error');
throw PlaybackSyncError( throw PlaybackSyncError(

View file

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/features/player/core/abs_audio_player.dart'; import 'package:vaani/features/player/core/abs_audio_player.dart';
@ -211,7 +210,7 @@ class PlaybackReporter {
_logger.fine('cancelled timer'); _logger.fine('cancelled timer');
} }
void _responseErrorHandler(http.Response response, [error]) { void _responseErrorHandler<T>(ApiResponse response, [error]) {
if (response.statusCode != 200) { if (response.statusCode != 200) {
_logger.severe('Error with api: ${response.obfuscate()}, $error'); _logger.severe('Error with api: ${response.obfuscate()}, $error');
throw PlaybackSyncError( throw PlaybackSyncError(

View file

@ -1,5 +1,4 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk; import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
import 'package:vaani/features/settings/models/api_settings.dart'; import 'package:vaani/features/settings/models/api_settings.dart';
import 'package:vaani/features/settings/models/audiobookshelf_server.dart'; import 'package:vaani/features/settings/models/audiobookshelf_server.dart';
@ -98,32 +97,80 @@ extension ObfuscateApiSettings on ApiSettings {
} }
} }
extension ObfuscateRequest on http.BaseRequest { // extension ObfuscateRequest on http.BaseRequest {
http.BaseRequest obfuscate() { // http.BaseRequest obfuscate() {
// if (!kReleaseMode) {
// return this;
// }
// return http.Request(
// method,
// url.obfuscate(),
// );
// }
// }
// extension ObfuscateResponse on http.Response {
// http.Response obfuscate() {
// if (!kReleaseMode) {
// return this;
// }
// return http.Response(
// obfuscateBody(),
// statusCode,
// headers: headers,
// request: request?.obfuscate(),
// );
// }
// String obfuscateBody() {
// if (!kReleaseMode) {
// return body;
// }
// // replace any email addresses with emailObfuscated
// // replace any phone numbers with phoneObfuscated
// // replace any urls with urlObfuscated
// // replace any tokens with tokenObfuscated
// // token regex is `"token": "..."`
// return body
// .replaceAll(
// RegExp(r'(\b\w+@\w+\.\w+\b)|'
// r'(\b\d{3}-\d{3}-\d{4}\b)|'
// r'(\bhttps?://\S+\b)'),
// 'obfuscated',
// )
// .replaceAll(
// RegExp(r'"?token"?:?\s*"[^"]+"'),
// '"token": "tokenObfuscated"',
// );
// }
// }
extension ObfuscateRequest on shelfsdk.ApiRequest {
shelfsdk.ApiRequest obfuscate() {
if (!kReleaseMode) { if (!kReleaseMode) {
return this; return this;
} }
return http.Request( return shelfsdk.ApiRequest(
method, method: method,
url.obfuscate(), url: url,
); );
} }
} }
extension ObfuscateResponse on http.Response { extension ObfuscateResponse on shelfsdk.ApiResponse {
http.Response obfuscate() { shelfsdk.ApiResponse obfuscate() {
if (!kReleaseMode) { if (!kReleaseMode) {
return this; return this;
} }
return http.Response( return shelfsdk.BaseResponse(
obfuscateBody(),
statusCode, statusCode,
obfuscateBody(),
headers: headers, headers: headers,
request: request?.obfuscate(), request: request.obfuscate(),
); );
} }
String obfuscateBody() { dynamic obfuscateBody() {
if (!kReleaseMode) { if (!kReleaseMode) {
return body; return body;
} }

View file

@ -1,26 +1,26 @@
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/shared/extensions/obfuscation.dart'; import 'package:vaani/shared/extensions/obfuscation.dart';
final _logger = Logger('ErrorResponse'); final _logger = Logger('ErrorResponse');
class ErrorResponseHandler { class ErrorResponseHandler {
String? name; String? name;
http.Response _response; ApiResponse _response;
bool logRawResponse; bool logRawResponse;
ErrorResponseHandler({ ErrorResponseHandler({
this.name, this.name,
http.Response? response, dynamic response,
this.logRawResponse = false, this.logRawResponse = false,
}) : _response = response ?? http.Response('', 418); }) : _response = response ?? BaseResponse(418, '');
void storeError(http.Response response, [Object? error]) { void storeError(ApiResponse response, [Object? error]) {
if (logRawResponse) { if (logRawResponse) {
_logger.fine('for $name got response: ${response.obfuscate()}'); _logger.severe('for $name got response: ${response.obfuscate()}');
} }
_response = response; _response = response;
} }
http.Response get response => _response; ApiResponse get response => _response;
} }

View file

@ -116,7 +116,7 @@ dependencies:
tray_manager: ^0.5.2 tray_manager: ^0.5.2
icons_plus: ^5.0.0 icons_plus: ^5.0.0
# http_cache_client: ^1.0.4 # http_cache_client: ^1.0.4
# http_cache_isar_store: ^3.0.0-dev.1 # http_cache_hive_store: ^5.0.1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.9 build_runner: ^2.4.9

@ -1 +1 @@
Subproject commit e1848a42c27257146015a33e9427f197f522fe03 Subproject commit c4d69ada95a8ad2db367bb3c5efd181668ceca6d