-
-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(functions_client): Add SSE support to invoke method #905
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
library functions_client; | ||
|
||
export 'package:http/http.dart' show ByteStream; | ||
|
||
export 'src/functions_client.dart'; | ||
export 'src/types.dart'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ import 'dart:convert'; | |
import 'package:functions_client/src/constants.dart'; | ||
import 'package:functions_client/src/types.dart'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:http/http.dart'; | ||
import 'package:yet_another_json_isolate/yet_another_json_isolate.dart'; | ||
|
||
class FunctionsClient { | ||
|
@@ -44,6 +43,20 @@ class FunctionsClient { | |
/// [headers]: object representing the headers to send with the request | ||
/// | ||
/// [body]: the body of the request | ||
/// | ||
/// ```dart | ||
/// // Call a standard function | ||
/// final response = await supabase.functions.invoke('hello-world'); | ||
/// print(response.data); | ||
/// | ||
/// // Listen to Server Sent Events | ||
/// final response = await supabase.functions.invoke('sse-function'); | ||
/// response.data | ||
/// .transform(const Utf8Decoder()) | ||
/// .listen((val) { | ||
/// print(val); | ||
/// }); | ||
/// ``` | ||
Future<FunctionResponse> invoke( | ||
String functionName, { | ||
Map<String, String>? headers, | ||
|
@@ -52,67 +65,42 @@ class FunctionsClient { | |
}) async { | ||
final bodyStr = body == null ? null : await _isolate.encode(body); | ||
|
||
late final Response response; | ||
final uri = Uri.parse('$_url/$functionName'); | ||
|
||
final finalHeaders = <String, String>{ | ||
..._headers, | ||
if (headers != null) ...headers | ||
}; | ||
|
||
switch (method) { | ||
case HttpMethod.post: | ||
response = await (_httpClient?.post ?? http.post)( | ||
uri, | ||
headers: finalHeaders, | ||
body: bodyStr, | ||
); | ||
break; | ||
|
||
case HttpMethod.get: | ||
response = await (_httpClient?.get ?? http.get)( | ||
uri, | ||
headers: finalHeaders, | ||
); | ||
break; | ||
|
||
case HttpMethod.put: | ||
response = await (_httpClient?.put ?? http.put)( | ||
uri, | ||
headers: finalHeaders, | ||
body: bodyStr, | ||
); | ||
break; | ||
|
||
case HttpMethod.delete: | ||
response = await (_httpClient?.delete ?? http.delete)( | ||
uri, | ||
headers: finalHeaders, | ||
); | ||
break; | ||
|
||
case HttpMethod.patch: | ||
response = await (_httpClient?.patch ?? http.patch)( | ||
uri, | ||
headers: finalHeaders, | ||
body: bodyStr, | ||
); | ||
break; | ||
} | ||
final request = http.Request(method.name, uri); | ||
|
||
finalHeaders.forEach((key, value) { | ||
request.headers[key] = value; | ||
}); | ||
if (bodyStr != null) request.body = bodyStr; | ||
final response = await (_httpClient?.send(request) ?? request.send()); | ||
final responseType = (response.headers['Content-Type'] ?? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the documentation it says, that all headers are converted to lowercase. So the first check should never succeed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I deleted the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still think the documentation is correct, but we just skip the translation when mocking the response object directly. So just keep it. |
||
response.headers['content-type'] ?? | ||
'text/plain') | ||
.split(';')[0] | ||
.trim(); | ||
|
||
final data = switch (responseType) { | ||
'application/json' => response.bodyBytes.isEmpty | ||
final dynamic data; | ||
|
||
if (responseType == 'application/json') { | ||
final bodyBytes = await response.stream.toBytes(); | ||
data = bodyBytes.isEmpty | ||
? "" | ||
: await _isolate.decode(utf8.decode(response.bodyBytes)), | ||
'application/octet-stream' => response.bodyBytes, | ||
_ => utf8.decode(response.bodyBytes), | ||
}; | ||
: await _isolate.decode(utf8.decode(bodyBytes)); | ||
} else if (responseType == 'application/octet-stream') { | ||
data = await response.stream.toBytes(); | ||
} else if (responseType == 'text/event-stream') { | ||
data = response.stream; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, I thought about adding data = response.stream.transform(const Utf8Decoder()); |
||
} else { | ||
final bodyBytes = await response.stream.toBytes(); | ||
data = utf8.decode(bodyBytes); | ||
} | ||
|
||
if (200 <= response.statusCode && response.statusCode < 300) { | ||
return FunctionResponse(data: data, status: response.statusCode); | ||
} else { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this, because I thought people might want to type cast the
response.data
asByteStream
.