Skip to content

Commit c629e9c

Browse files
committed
feat: support MultipartRequest in functions invoke
1 parent 6323f9f commit c629e9c

File tree

3 files changed

+74
-9
lines changed

3 files changed

+74
-9
lines changed

packages/functions_client/lib/src/functions_client.dart

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:convert';
33
import 'package:functions_client/src/constants.dart';
44
import 'package:functions_client/src/types.dart';
55
import 'package:http/http.dart' as http;
6+
import 'package:http/http.dart' show MultipartRequest;
67
import 'package:yet_another_json_isolate/yet_another_json_isolate.dart';
78

89
class FunctionsClient {
@@ -38,11 +39,17 @@ class FunctionsClient {
3839

3940
/// Invokes a function
4041
///
41-
/// [functionName] - the name of the function to invoke
42+
/// [functionName] is the name of the function to invoke
4243
///
43-
/// [headers]: object representing the headers to send with the request
44+
/// [headers] to send with the request
45+
///
46+
/// [body] of the request when [files] is null and can be of type String
47+
/// or an Object that is encodable to JSON with `jsonEncode`.
48+
/// If [files] is not null, [body] represents the fields of the
49+
/// [MultipartRequest] and must be be of type `Map<String, String>`.
50+
///
51+
/// [files] to send in a `MultipartRequest`. [body] is used for the fields.
4452
///
45-
/// [body]: the body of the request
4653
///
4754
/// ```dart
4855
/// // Call a standard function
@@ -70,12 +77,11 @@ class FunctionsClient {
7077
Future<FunctionResponse> invoke(
7178
String functionName, {
7279
Map<String, String>? headers,
73-
Map<String, dynamic>? body,
80+
Object? body,
81+
Iterable<http.MultipartFile>? files,
7482
Map<String, dynamic>? queryParameters,
7583
HttpMethod method = HttpMethod.post,
7684
}) async {
77-
final bodyStr = body == null ? null : await _isolate.encode(body);
78-
7985
final uri = Uri.parse('$_url/$functionName')
8086
.replace(queryParameters: queryParameters);
8187

@@ -84,12 +90,36 @@ class FunctionsClient {
8490
if (headers != null) ...headers
8591
};
8692

87-
final request = http.Request(method.name, uri);
93+
final http.BaseRequest request;
94+
if (files != null) {
95+
assert(
96+
body == null || body is Map<String, String>,
97+
'body must be of type Map',
98+
);
99+
final fields = body as Map<String, String>?;
100+
101+
request = http.MultipartRequest(method.name, uri)
102+
..fields.addAll(fields ?? {})
103+
..files.addAll(files);
104+
} else {
105+
final bodyRequest = http.Request(method.name, uri);
106+
107+
final String? bodyStr;
108+
if (body == null) {
109+
bodyStr = null;
110+
} else if (body is String) {
111+
bodyStr = body;
112+
} else {
113+
bodyStr = await _isolate.encode(body);
114+
}
115+
if (bodyStr != null) bodyRequest.body = bodyStr;
116+
request = bodyRequest;
117+
}
88118

89119
finalHeaders.forEach((key, value) {
90120
request.headers[key] = value;
91121
});
92-
if (bodyStr != null) request.body = bodyStr;
122+
93123
final response = await (_httpClient?.send(request) ?? request.send());
94124
final responseType = (response.headers['Content-Type'] ??
95125
response.headers['content-type'] ??

packages/functions_client/test/custom_http_client.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,22 @@ class CustomHttpClient extends BaseClient {
3232
"Content-Type": "text/event-stream",
3333
});
3434
} else {
35+
final Stream<List<int>> stream;
36+
if (request is MultipartRequest) {
37+
stream = Stream.value(
38+
utf8.encode(jsonEncode([
39+
for (final file in request.files)
40+
{
41+
"name": file.field,
42+
"content": await file.finalize().bytesToString()
43+
}
44+
])),
45+
);
46+
} else {
47+
stream = Stream.value(utf8.encode(jsonEncode({"key": "Hello World"})));
48+
}
3549
return StreamedResponse(
36-
Stream.value(utf8.encode(jsonEncode({"key": "Hello World"}))),
50+
stream,
3751
200,
3852
request: request,
3953
headers: {

packages/functions_client/test/functions_dart_test.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:convert';
22

33
import 'package:functions_client/src/functions_client.dart';
44
import 'package:functions_client/src/types.dart';
5+
import 'package:http/http.dart';
56
import 'package:test/test.dart';
67
import 'package:yet_another_json_isolate/yet_another_json_isolate.dart';
78

@@ -43,6 +44,26 @@ void main() {
4344
expect(res.status, 200);
4445
});
4546

47+
test('function call with files', () async {
48+
final fileName = "file.txt";
49+
final fileContent = "Hello World";
50+
final res = await functionsCustomHttpClient.invoke(
51+
'function1',
52+
queryParameters: {'key': 'value'},
53+
files: [
54+
MultipartFile.fromString(fileName, fileContent),
55+
],
56+
);
57+
58+
final request = customHttpClient.receivedRequests.last;
59+
60+
expect(request.url.queryParameters, {'key': 'value'});
61+
expect(res.data, [
62+
{'name': fileName, 'content': fileContent}
63+
]);
64+
expect(res.status, 200);
65+
});
66+
4667
test('dispose isolate', () async {
4768
await functionsCustomHttpClient.dispose();
4869
expect(functionsCustomHttpClient.invoke('function'), throwsStateError);

0 commit comments

Comments
 (0)