Skip to content

Commit

Permalink
Merge pull request #61 from ArunPrakashG/feature/execute-middleware
Browse files Browse the repository at this point in the history
[FEATURE] Execution Middleware Override
  • Loading branch information
ArunPrakashG authored Apr 20, 2024
2 parents d1d63b3 + b102f5f commit 93d8656
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 9 deletions.
2 changes: 1 addition & 1 deletion lib/src/bootstrap_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BootstrapBuilder {
_middlewares = config.middlewares;
}

Duration _defaultRequestTimeout = kDefaultRequestTimeout; // 60 seconds
Duration _defaultRequestTimeout = DEFAULT_REQUEST_TIMEOUT; // 60 seconds
bool Function(dynamic)? _responsePreprocessorDelegate;
IAuthorization? _defaultAuthorization;
String? _defaultUserAgent;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/client_configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'constants.dart';
@immutable
final class BootstrapConfiguration {
const BootstrapConfiguration({
this.requestTimeout = kDefaultRequestTimeout,
this.requestTimeout = DEFAULT_REQUEST_TIMEOUT,
this.responsePreprocessorDelegate,
this.defaultAuthorization,
this.defaultUserAgent,
Expand Down
3 changes: 2 additions & 1 deletion lib/src/constants.dart
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
const Duration kDefaultRequestTimeout = Duration(seconds: 30);
const Duration DEFAULT_REQUEST_TIMEOUT = Duration(seconds: 30);
const String MIDDLEWARE_HEADER_KEY = 'X-Local-Middleware';
1 change: 1 addition & 0 deletions lib/src/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum RequestErrorType {
requestCancelled,
invalidStatusCode,
middlewareAborted,
middlewareExecutionFailed,
}

enum SearchType {
Expand Down
63 changes: 60 additions & 3 deletions lib/src/internal_requester.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
part of 'wordpress_client_base.dart';

typedef MiddlwareResponseTransformer = WordpressRawResponse Function(
MiddlewareRawResponse data,
);

final class InternalRequester extends IRequestExecutor {
InternalRequester.configure(
this._baseUrl, [
Expand Down Expand Up @@ -94,7 +98,7 @@ final class InternalRequester extends IRequestExecutor {

@override
Future<WordpressRawResponse> execute(WordpressRequest request) async {
final headers = request.headers ?? <String, dynamic>{};
final requestHeaders = request.headers ?? <String, dynamic>{};

if (request.requireAuth) {
final authorizer = () {
Expand Down Expand Up @@ -127,7 +131,7 @@ final class InternalRequester extends IRequestExecutor {
);
}

headers[authResult.key] = authResult.value;
requestHeaders[authResult.key] = authResult.value;
}

final requestUrl = () {
Expand All @@ -140,6 +144,44 @@ final class InternalRequester extends IRequestExecutor {

_triggerStatistics(requestUrl);

final result = await executeGuarded(
function: () async {
return _executeMiddlewareEvent(
request: request,
transformer: (data) {
final headers = <String, dynamic>{
MIDDLEWARE_HEADER_KEY: true,
};

headers.addAllIfNotNull(data.headers);

return WordpressRawResponse(
data: data,
code: data.statusCode,
headers: headers,
requestHeaders: requestHeaders,
extra: data.extra ?? <String, dynamic>{},
message:
data.message ?? 'Middleware event executed successfully.',
);
},
);
},
onError: (error, stackTrace) async {
return WordpressRawResponse(
data: null,
requestHeaders: requestHeaders,
code: -RequestErrorType.middlewareExecutionFailed.index,
message: 'Middleware execution failed.',
);
},
);

if (result != null &&
result.code != -RequestErrorType.middlewareExecutionFailed.index) {
return result;
}

final watch = Stopwatch();

Future<Response<dynamic>> run() async {
Expand All @@ -159,7 +201,7 @@ final class InternalRequester extends IRequestExecutor {
method: request.method.name,
sendTimeout: request.sendTimeout,
receiveTimeout: request.receiveTimeout,
headers: headers,
headers: requestHeaders,
),
);
} finally {
Expand All @@ -184,6 +226,21 @@ final class InternalRequester extends IRequestExecutor {
);
}

Future<WordpressRawResponse?> _executeMiddlewareEvent({
required WordpressRequest request,
required MiddlwareResponseTransformer transformer,
}) async {
for (final middleware in _middlewares) {
final result = await middleware.onExecute(request);

if (result.hasData) {
return transformer(result);
}
}

return null;
}

Dio _createDioClient() {
final client = Dio(
BaseOptions(
Expand Down
11 changes: 11 additions & 0 deletions lib/src/middleware/delegated_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ typedef OnResponseDelegate = Future<WordpressRawResponse> Function(

typedef InitializeDelegate = Future<void> Function();
typedef OnRemovedDelegate = Future<void> Function();
typedef OnExecuteDelegate = Future<MiddlewareRawResponse> Function(
WordpressRequest request,
);

final class DelegatedMiddleware extends IWordpressMiddleware {
const DelegatedMiddleware({
required this.onRequestDelegate,
required this.onResponseDelegate,
this.initializeDelegate,
this.onExecuteDelegate,
this.onRemovedDelegate,
});

final InitializeDelegate? initializeDelegate;
final OnRequestDelegate onRequestDelegate;
final OnResponseDelegate onResponseDelegate;
final OnRemovedDelegate? onRemovedDelegate;
final OnExecuteDelegate? onExecuteDelegate;

@override
Future<void> onLoad() async {
Expand All @@ -39,6 +44,12 @@ final class DelegatedMiddleware extends IWordpressMiddleware {
return onRequestDelegate(request);
}

@override
Future<MiddlewareRawResponse> onExecute(WordpressRequest request) async {
return await onExecuteDelegate?.call(request) ??
MiddlewareRawResponse.defaultInstance();
}

@override
Future<WordpressRawResponse> onResponse(WordpressRawResponse response) async {
return onResponseDelegate(response);
Expand Down
1 change: 1 addition & 0 deletions lib/src/middleware/middleware_exports.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export 'delegated_middleware.dart';
export 'models/middleware_raw_response.dart';
export 'wordpress_middleware_base.dart';
26 changes: 26 additions & 0 deletions lib/src/middleware/models/middleware_raw_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
final class MiddlewareRawResponse {
const MiddlewareRawResponse({
required this.statusCode,
required this.body,
this.message,
this.headers,
this.extra,
});

factory MiddlewareRawResponse.defaultInstance() {
return const MiddlewareRawResponse(statusCode: -99, body: null);
}

final int statusCode;
final Map<String, dynamic>? headers;
final Map<String, dynamic>? extra;
final dynamic body;
final String? message;

bool get hasData => body != null && statusCode >= 200 && statusCode < 300;

@override
String toString() {
return 'MiddlewareRawResponse(statusCode: $statusCode, headers: $headers, body: $body, message: $message, extra: $extra)';
}
}
20 changes: 19 additions & 1 deletion lib/src/middleware/wordpress_middleware_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,31 @@ import 'dart:async';

import '../library_exports.dart';

abstract base class IWordpressMiddleware {
/// The base interface for WordPress middleware.
abstract class IWordpressMiddleware {
const IWordpressMiddleware();

/// The name of the middleware.
String get name;

/// Called when the middleware is loaded.
Future<void> onLoad();

/// Called before sending a request to the WordPress server.
Future<WordpressRequest> onRequest(WordpressRequest request);

/// Called before executing a request to the WordPress server.
///
/// By default, it returns a [MiddlewareRawResponse] with default values.
///
/// This can used for returning a custom response (cached response), or to cancel the request (By throwing an exception).
Future<MiddlewareRawResponse> onExecute(WordpressRequest request) async {
return MiddlewareRawResponse.defaultInstance();
}

/// Called after receiving a response from the WordPress server.
Future<WordpressRawResponse> onResponse(WordpressRawResponse response);

/// Called when the middleware is unloaded.
Future<void> onUnload();
}
4 changes: 2 additions & 2 deletions lib/src/requests/wordpress_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ final class WordpressRequest {
this.requireAuth = false,
this.authorization,
this.validator,
this.sendTimeout = kDefaultRequestTimeout,
this.receiveTimeout = kDefaultRequestTimeout,
this.sendTimeout = DEFAULT_REQUEST_TIMEOUT,
this.receiveTimeout = DEFAULT_REQUEST_TIMEOUT,
});

/// The request url.
Expand Down
3 changes: 3 additions & 0 deletions lib/src/responses/wordpress_raw_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
import 'package:meta/meta.dart';

import '../../wordpress_client.dart';
import '../constants.dart';
import '../utilities/helpers.dart';
import 'wordpress_error.dart';

Expand Down Expand Up @@ -71,6 +72,8 @@ final class WordpressRawResponse {
/// Returns true if the given [code] is not in between 200 to 399 range. A convenience method for `!isSuccessful`.
bool get isFailure => !isSuccessful;

bool get isMiddlewareResponse => headers.containsKey(MIDDLEWARE_HEADER_KEY);

dynamic operator [](dynamic key) {
if (data == null) {
throw NullReferenceException('Response is null.');
Expand Down
1 change: 1 addition & 0 deletions lib/src/wordpress_client_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:dio/dio.dart';
import 'package:path/path.dart';

import 'bootstrap_builder.dart';
import 'constants.dart';
import 'interface/application_passwords.dart';
import 'interface/category.dart';
import 'interface/comments.dart';
Expand Down

0 comments on commit 93d8656

Please sign in to comment.