Line data Source code
1 : import 'package:flutter/foundation.dart';
2 :
3 : /// Base class for all background task middleware.
4 : ///
5 : /// Middleware allows you to intercept and modify task configurations
6 : /// globally before they are executed by the native engine.
7 : @immutable
8 : abstract class Middleware {
9 2 : const Middleware();
10 :
11 : /// Convert to map for platform channel.
12 : Map<String, dynamic> toMap();
13 : }
14 :
15 : /// Middleware that adds HTTP headers to all matching requests.
16 : ///
17 : /// Use this to globally inject authentication tokens or custom
18 : /// user-agent strings into native HTTP workers.
19 : class HeaderMiddleware extends Middleware {
20 1 : const HeaderMiddleware({
21 : required this.headers,
22 : this.urlPattern,
23 : });
24 :
25 : /// Map of headers to add.
26 : final Map<String, String> headers;
27 :
28 : /// Optional regex pattern to match URLs.
29 : ///
30 : /// If provided, headers are only added to HTTP workers whose URL
31 : /// matches this pattern. If null, applies to all HTTP workers.
32 : final String? urlPattern;
33 :
34 2 : @override
35 : Map<String, dynamic> toMap() {
36 2 : return {
37 : 'type': 'header',
38 2 : 'headers': headers,
39 2 : 'urlPattern': urlPattern,
40 : };
41 : }
42 : }
43 :
44 : /// Middleware that POSTs task execution metadata to a custom endpoint
45 : /// after each task completes (success or failure).
46 : ///
47 : /// Unlike [HeaderMiddleware] and [RemoteConfigMiddleware] (which modify worker
48 : /// config before execution), `LoggingMiddleware` is a **post-execution hook**.
49 : /// It fires a fire-and-forget HTTP POST and never affects the worker result.
50 : ///
51 : /// ## Payload
52 : ///
53 : /// ```json
54 : /// {
55 : /// "taskId": "my-sync-task",
56 : /// "workerClassName": "HttpDownloadWorker",
57 : /// "success": true,
58 : /// "timestamp": 1712345678000,
59 : /// "durationMs": 1234,
60 : /// "message": "Downloaded 5.2 MB",
61 : /// "workerConfig": { ... } // only when includeConfig: true
62 : /// }
63 : /// ```
64 : ///
65 : /// ## Usage
66 : ///
67 : /// ```dart
68 : /// await NativeWorkManager.registerMiddleware(
69 : /// LoggingMiddleware(
70 : /// logUrl: 'https://logs.example.com/tasks',
71 : /// includeConfig: false,
72 : /// ),
73 : /// );
74 : /// ```
75 : ///
76 : /// Network errors from the log POST are silently swallowed — a logging
77 : /// failure never causes a task to be marked as failed.
78 : class LoggingMiddleware extends Middleware {
79 1 : const LoggingMiddleware({
80 : required this.logUrl,
81 : this.includeConfig = false,
82 : });
83 :
84 : /// URL to POST execution logs to after each task completes.
85 : final String logUrl;
86 :
87 : /// Whether to include the worker configuration map in the log payload.
88 : ///
89 : /// Disable (default) to avoid leaking sensitive config values (URLs,
90 : /// credentials, file paths) to your logging endpoint.
91 : final bool includeConfig;
92 :
93 2 : @override
94 : Map<String, dynamic> toMap() {
95 2 : return {
96 : 'type': 'logging',
97 2 : 'logUrl': logUrl,
98 2 : 'includeConfig': includeConfig,
99 : };
100 : }
101 : }
102 :
103 : /// Middleware that injects remote configuration values into worker configs
104 : /// at execution time.
105 : ///
106 : /// Use this to control worker behaviour dynamically at runtime — no app
107 : /// update required. Values can come from any source: Firebase Remote Config,
108 : /// AWS AppConfig, LaunchDarkly, or a plain REST endpoint.
109 : ///
110 : /// ## Usage
111 : ///
112 : /// ```dart
113 : /// // 1. Fetch values from your config source (e.g. Firebase Remote Config)
114 : /// final rc = FirebaseRemoteConfig.instance;
115 : /// await rc.fetchAndActivate();
116 : ///
117 : /// // 2. Register middleware once at startup (or refresh on config change)
118 : /// await NativeWorkManager.registerMiddleware(
119 : /// RemoteConfigMiddleware(
120 : /// values: {
121 : /// 'timeout': rc.getInt('download_timeout_seconds'),
122 : /// 'maxRetries': rc.getInt('max_retries'),
123 : /// },
124 : /// workerType: 'HttpDownload', // optional: only targets HttpDownloadWorker
125 : /// ),
126 : /// );
127 : /// ```
128 : ///
129 : /// To refresh the config (e.g. after a Remote Config fetch), simply call
130 : /// `registerMiddleware` again with updated [values] — the native side
131 : /// replaces the previous entry for the `remoteConfig` type.
132 : ///
133 : /// ## How it works
134 : ///
135 : /// Each key in [values] is injected directly into the native worker config
136 : /// map, overriding any existing value with the same name. If [workerType]
137 : /// is provided, the middleware only applies to workers whose class name
138 : /// contains that string (case-insensitive substring match).
139 : class RemoteConfigMiddleware extends Middleware {
140 2 : const RemoteConfigMiddleware({
141 : required this.values,
142 : this.workerType,
143 : });
144 :
145 : /// Config values to inject into matching worker configurations.
146 : ///
147 : /// Keys map directly to worker config fields. Supported value types:
148 : /// `String`, `int`, `double`, `bool`. Nested maps are not supported.
149 : final Map<String, dynamic> values;
150 :
151 : /// Optional worker class name filter (case-insensitive substring match).
152 : ///
153 : /// When set, only workers whose class name contains this string receive
154 : /// the injected values. For example, `'HttpDownload'` targets only
155 : /// `HttpDownloadWorker`. When `null`, all workers are affected.
156 : final String? workerType;
157 :
158 3 : @override
159 3 : Map<String, dynamic> toMap() => {
160 3 : 'type': 'remoteConfig',
161 6 : 'values': values,
162 9 : if (workerType != null) 'workerType': workerType,
163 : };
164 : }
|