From 252ada0d2877eae14f7cb00dbdeed6ad11382484 Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Sun, 5 Apr 2026 21:53:16 +0300 Subject: [PATCH 1/2] feat(network): add configureDriver() plugin hook for Dio access (#32) Enables SDK integrations (sentry_dio, cert pinning) that need raw Dio instance access. Configurator callback pattern keeps Dio conceptually owned by the driver while allowing one-time setup. --- CHANGELOG.md | 1 + doc/basics/http-client.md | 14 +++++++++ .../network/drivers/dio_network_driver.dart | 15 ++++++++++ .../references/http-network.md | 14 +++++++++ test/network/network_test.dart | 29 +++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2d585..543461b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### ✨ Features - **Router Observers**: `MagicRouter.instance.addObserver()` enables NavigatorObserver integration for analytics/monitoring (Sentry, Firebase Analytics, custom observers). Observers are passed to GoRouter automatically. (#31) +- **Network Driver Plugin Hook**: `DioNetworkDriver.configureDriver()` exposes the underlying Dio instance for SDK integrations (sentry_dio, certificate pinning, custom adapters). (#32) ## [1.0.0-alpha.6] - 2026-04-05 diff --git a/doc/basics/http-client.md b/doc/basics/http-client.md index 034ba09..d4a254f 100644 --- a/doc/basics/http-client.md +++ b/doc/basics/http-client.md @@ -299,3 +299,17 @@ class NetworkServiceProvider extends ServiceProvider { } } ``` + +### Driver Plugin Hook + +For SDK integrations that need direct Dio access (e.g., `sentry_dio` for performance tracing, certificate pinning), use `configureDriver()`: + +```dart +final driver = Magic.make('network'); +driver.configureDriver((dio) { + dio.addSentry(); // Full Sentry performance tracing +}); +``` + +> [!NOTE] +> `configureDriver()` is specific to `DioNetworkDriver`. Resolve using `Magic.make('network')` to access it. diff --git a/lib/src/network/drivers/dio_network_driver.dart b/lib/src/network/drivers/dio_network_driver.dart index 6a2a9fb..f3ea815 100644 --- a/lib/src/network/drivers/dio_network_driver.dart +++ b/lib/src/network/drivers/dio_network_driver.dart @@ -109,6 +109,21 @@ class DioNetworkDriver implements NetworkDriver { ); } + /// Configure the underlying Dio instance directly. + /// + /// Use this for SDK integrations that need raw Dio access + /// (e.g., `sentry_dio`, certificate pinning, custom adapters). + /// + /// ```dart + /// final driver = Magic.make('network'); + /// driver.configureDriver((dio) { + /// dio.addSentry(); + /// }); + /// ``` + void configureDriver(void Function(Dio dio) configurator) { + configurator(_dio); + } + // --------------------------------------------------------------------------- // RESTful Resource Methods // --------------------------------------------------------------------------- diff --git a/skills/magic-framework/references/http-network.md b/skills/magic-framework/references/http-network.md index b812fc6..5d5c2ac 100644 --- a/skills/magic-framework/references/http-network.md +++ b/skills/magic-framework/references/http-network.md @@ -315,6 +315,20 @@ await Magic.init(); Magic.make('network').addInterceptor(LoggingInterceptor()); ``` +## Driver Plugin Hook + +For SDK integrations that need direct Dio access (e.g., `sentry_dio`, certificate pinning), use `configureDriver()`: + +```dart +// In a service provider boot() +final driver = Magic.make('network'); +driver.configureDriver((dio) { + dio.addSentry(); // sentry_dio integration +}); +``` + +This is a `DioNetworkDriver`-specific method (not on the `NetworkDriver` contract). Cast or resolve as `DioNetworkDriver` to access it. + ## ValidatesRequests Mixin Use the `ValidatesRequests` mixin in controllers to simplify validation error mapping from HTTP responses. diff --git a/test/network/network_test.dart b/test/network/network_test.dart index 4487979..337cd03 100644 --- a/test/network/network_test.dart +++ b/test/network/network_test.dart @@ -104,6 +104,35 @@ void main() { // Should not throw driver.addInterceptor(interceptor); }); + + test('configureDriver() applies configurator to Dio instance', () { + var called = false; + driver.configureDriver((dio) { + called = true; + dio.interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) => handler.next(options), + ), + ); + }); + + expect(called, isTrue); + }); + + test('configureDriver() allows multiple configurators', () { + var firstCalled = false; + var secondCalled = false; + + driver.configureDriver((dio) { + firstCalled = true; + }); + driver.configureDriver((dio) { + secondCalled = true; + }); + + expect(firstCalled, isTrue); + expect(secondCalled, isTrue); + }); }); } From 2a1e074362843bde13be0c9ec8511547361e230e Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Sun, 5 Apr 2026 22:06:14 +0300 Subject: [PATCH 2/2] fix(test): assert observable Dio mutations in configureDriver tests (#35) Tests now verify interceptor count changes on the driver-owned Dio instance instead of only asserting callbacks were invoked. Ensures configurator receives the actual Dio instance, not a fresh one. --- test/network/network_test.dart | 36 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/test/network/network_test.dart b/test/network/network_test.dart index 337cd03..0be9636 100644 --- a/test/network/network_test.dart +++ b/test/network/network_test.dart @@ -105,10 +105,11 @@ void main() { driver.addInterceptor(interceptor); }); - test('configureDriver() applies configurator to Dio instance', () { - var called = false; + test('configureDriver() mutates the driver-owned Dio instance', () { + late int countBefore; + driver.configureDriver((dio) { - called = true; + countBefore = dio.interceptors.length; dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) => handler.next(options), @@ -116,22 +117,37 @@ void main() { ); }); - expect(called, isTrue); + // Verify interceptor persists on the same Dio instance. + late int countAfter; + driver.configureDriver((dio) { + countAfter = dio.interceptors.length; + }); + + expect(countAfter, countBefore + 1); }); test('configureDriver() allows multiple configurators', () { - var firstCalled = false; - var secondCalled = false; + late int countAfterFirst; + late int countAfterSecond; driver.configureDriver((dio) { - firstCalled = true; + dio.interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) => handler.next(options), + ), + ); + countAfterFirst = dio.interceptors.length; }); driver.configureDriver((dio) { - secondCalled = true; + dio.interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) => handler.next(options), + ), + ); + countAfterSecond = dio.interceptors.length; }); - expect(firstCalled, isTrue); - expect(secondCalled, isTrue); + expect(countAfterSecond, countAfterFirst + 1); }); }); }