Skip to content

Commit

Permalink
Merge pull request #102 from mysCod3r/feature/ys/improve-network-model
Browse files Browse the repository at this point in the history
Improve Network Model
  • Loading branch information
VB10 authored Sep 2, 2024
2 parents 1bbed70 + ae776ca commit 04a2019
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 619 deletions.
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,63 @@ INetworkManager networkManager = NetworkManage<Null or UserErrorModel>(isEnable

### **Model Parse** ⚔️

First, you have to provide the parse model, then the result model. (Result model could be a list, model or primitive)
- First, you have to provide the parse model, then the result model. (Result model could be a list, model or primitive)
- "You have two options: you can use either the `send` method or the `sendRequest` method."

### `send`
```dart
final response =
await networkManager.send<Todo, List<Todo>>("/todos", parseModel: Todo(), method: RequestType.GET);
final response = await networkManager.send<Todo, List<Todo>>(
"/todos",
parseModel: Todo(),
method: RequestType.GET,
);
```

### `sendRequest`

This method is designed to improve the 'send' method. The response is a 'NetworkResult' object, which is a `sealed class`. You can use it however you like, either with pattern matching or other methods.

```dart
final response = await networkManager.sendRequest<Todo, List<Todo>>(
'/todos',
parseModel: Todo(),
method: RequestType.GET,
);
// usage 1:
response.fold(
onSuccess: (data) {
// handle success case
},
onError: (error) {
// handle error case
},
);
// usage 2
final _ = switch (response) {
NetworkSuccessResult(:final data) => data,
NetworkErrorResult(:final error) => error,
};
// usage 3
if (response is NetworkSuccessResult<List<Todo>, EmptyModel>) {
final List<Todo> todos = response.data;
// use the data
}
// You can check the response using the `isSuccess` and `isError` getters.
if (response.isSuccess) {
final data = (response as NetworkSuccessResult<List<Todo>,EmptyModel>).data;
// handle success case
return;
}
if (response.isError) {
final error = (response as NetworkErrorResult<List<Todo>,EmptyModel>).error;
// handle error case
return;
}
```

### **Base Headers** 📍
Expand Down
52 changes: 26 additions & 26 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ packages:
dependency: transitive
description:
name: dio
sha256: "77befdddf51050e1635a04d2bcfff230089ce7294e642d00da58cd079c0de0c8"
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
url: "https://pub.dev"
source: hosted
version: "5.5.0"
version: "5.5.0+1"
dio_web_adapter:
dependency: transitive
description:
Expand Down Expand Up @@ -140,10 +140,10 @@ packages:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "2.0.1"
lints:
dependency: transitive
description:
Expand All @@ -156,10 +156,10 @@ packages:
dependency: transitive
description:
name: logger
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4
sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.4.0"
matcher:
dependency: transitive
description:
Expand Down Expand Up @@ -196,18 +196,18 @@ packages:
dependency: transitive
description:
name: path_provider
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.4"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev"
source: hosted
version: "2.2.6"
version: "2.2.4"
path_provider_foundation:
dependency: transitive
description:
Expand Down Expand Up @@ -236,10 +236,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.3.0"
platform:
dependency: transitive
description:
Expand Down Expand Up @@ -276,50 +276,50 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
version: "2.2.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.5.0"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.4.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
sky_engine:
dependency: transitive
description: flutter
Expand Down Expand Up @@ -429,5 +429,5 @@ packages:
source: hosted
version: "1.0.4"
sdks:
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.22.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"
22 changes: 19 additions & 3 deletions lib/src/interface/i_network_manager.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'package:dio/dio.dart';
import 'package:vexana/src/interface/index.dart';
import 'package:vexana/src/mixin/index.dart';
import 'package:vexana/src/model/enum/request_type.dart';
import 'package:vexana/vexana.dart';

/// The `INetworkManager` interface is used to define the methods that are used
/// to send HTTP requests to a server. It extends the `NetworkManagerParameters`
Expand Down Expand Up @@ -42,6 +40,24 @@ abstract class INetworkManager<E extends INetworkModel<E>>
bool isErrorDialog = false,
});

/// The sendRequest method is used to send an HTTP request
/// and handle the response. It supports various HTTP methods and
/// can parse the response into a specified model. This method also provides
/// options for handling progress, cancellation, and caching.
Future<NetworkResult<R, E>> sendRequest<T extends INetworkModel<T>, R>(
String path, {
required T parseModel,
required RequestType method,
String? urlSuffix,
Map<String, dynamic>? queryParameters,
Options? options,
Duration? expiration,
dynamic data,
ProgressCallback? onReceiveProgress,
CancelToken? cancelToken,
bool isErrorDialog = false,
});

/// The `downloadFileSimple` method is used to download a file from a
/// specified `path` using a simple HTTP GET request. It
/// returns a `Future<Response<List<int>?>>` which represents the response
Expand Down
24 changes: 24 additions & 0 deletions lib/src/mixin/network_manager_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ mixin NetworkManagerCache<E extends INetworkModel<E>>
);
}

/// The loadFromCache method retrieves data from the cache based on the
/// request type and expiration duration. If the data is found and not
/// expired, it parses the data into the specified model and returns a
/// success result. If the data is not found or is expired,
/// it returns null or an error result.
Future<NetworkResult<R, E>?> loadFromCache<R, T extends INetworkModel<T>>({
required Duration? expiration,
required RequestType type,
required T responseModel,
}) async {
if (expiration == null) return null;
final cacheDataString = await _fetchOnlyData(type);
if (cacheDataString == null) return null;

final model = parseUserResponseData<R, T>(
NetworkManagerUtil.decodeBodyWithCompute(cacheDataString),
responseModel,
);

if (model is R) return NetworkSuccessResult(model);
final error = ErrorModel<E>.parseError();
return NetworkErrorResult(error);
}

/// Write data to database
Future<void> writeAll(
Duration? expiration,
Expand Down
56 changes: 56 additions & 0 deletions lib/src/mixin/network_manager_core_operation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,60 @@ mixin NetworkManagerCoreOperation<E extends INetworkModel<E>> {
_noNetworkTryCount = 0;
return onError.call(error);
}

/// The handleErrorResponse method handles network errors by either invoking
/// an error callback or retrying the request based on certain conditions.
/// If the network is unavailable and retries are enabled, it shows a custom
/// no-network widget and retries the request if the user opts to retry.
Future<NetworkResult<R, E>>
handleErrorResponse<T extends INetworkModel<T>, R>({
required String path,
required T parseModel,
required RequestType method,
required DioException error,
required NetworkResult<R, E> Function(DioException e) onError,
String? urlSuffix = '',
Map<String, dynamic>? queryParameters,
Options? options,
Duration? expiration,
dynamic data,
ProgressCallback? onReceiveProgress,
bool isErrorDialog = false,
CancelToken? cancelToken,
bool? forceUpdateDecode,
}) async {
if (!isErrorDialog ||
_noNetworkTryCount == NetworkManagerParameters.maxRetryCount) {
return onError.call(error);
}

_noNetworkTryCount = 0;
var isRetry = false;
await NoNetworkManager(
context: parameters.noNetwork?.context,
customNoNetworkWidget: parameters.noNetwork?.customNoNetwork,
onRetry: () {
isRetry = true;
},
isEnable: true,
).show();

if (isRetry) {
_noNetworkTryCount = _noNetworkTryCount + 1;

return instance.sendRequest(
path,
parseModel: parseModel,
method: method,
data: data,
queryParameters: queryParameters,
options: options,
isErrorDialog: isErrorDialog,
urlSuffix: urlSuffix,
);
}

_noNetworkTryCount = 0;
return onError.call(error);
}
}
31 changes: 31 additions & 0 deletions lib/src/mixin/network_manager_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ mixin NetworkManagerResponse<E extends INetworkModel<E>> {
);
}

/// The fetchSuccessResponse method parses the provided data into the
/// specified model and returns a NetworkResult. If the parsing is successful,
/// it returns a NetworkSuccessResult with the parsed model. If parsing fails,
/// it returns a NetworkErrorResult with an error model.
NetworkResult<R, E> fetchSuccessResponse<T extends INetworkModel<T>, R>({
required dynamic data,
required T parserModel,
}) {
final model = parseUserResponseData<R, T>(data, parserModel);
if (model != null) return NetworkSuccessResult(model);
return NetworkErrorResult(ErrorModel<E>.parseError());
}

/// Error response fetch to parse it
///
/// R: void or null for only show error
Expand All @@ -59,6 +72,24 @@ mixin NetworkManagerResponse<E extends INetworkModel<E>> {
);
}

/// The fetchErrorResponse method handles a DioException,
/// logs the error message if logging is enabled, and generates an error model
/// based on the exception and its response data.
/// It then returns a NetworkErrorResult containing the error model.
NetworkResult<R, E> fetchErrorResponse<R>(DioException exception) {
CustomLogger(
isEnabled: parameters.isEnableLogger,
data: exception.message,
).printError();

final errorResponse = exception.response;
var error = ErrorModel<E>.dioException(exception);
if (errorResponse != null) {
error = _generateErrorModel(error, errorResponse.data);
}
return NetworkErrorResult(error);
}

ErrorModel<E> _generateErrorModel(ErrorModel<E> error, dynamic data) {
if (errorModel == null) return error;

Expand Down
1 change: 1 addition & 0 deletions lib/src/model/index.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'empty_model.dart';
export 'error_model.dart';
export 'local_data.dart';
export 'network_result.dart';
export 'response_model.dart';
Loading

0 comments on commit 04a2019

Please sign in to comment.