Async interceptors (gRPC dart) - dart

I'm using gRPC generated clients for dart and I need to add Authorization header to every request. I know there's ClientInterceptor but I want to ask if there is any way to call async method in interceptor's method because I have this async method for retrieving token:
static Future<String> getToken() async {
final CognitoAuthSession session = await Amplify.Auth.fetchAuthSession(
options: CognitoSessionOptions(getAWSCredentials: true)
);
return session.userPoolTokens.idToken;
}

After some more googling I found out that MetadataProvider return FutureOr<void>, so you can call async methods.
Final interceptor's code
class AuthenticationInterceptor extends ClientInterceptor {
FutureOr<void> _provider(Map<String, String> metadata, String uri) async {
final token = await AuthenticationService.getToken();
metadata['Authorization'] = "Bearer $token";
}
#override
ResponseFuture<R> interceptUnary<Q, R>(ClientMethod<Q, R> method, Q request, CallOptions options, invoker) {
return super.interceptUnary(
method,
request,
options.mergedWith(CallOptions(providers: [_provider])),
invoker
);
}
}
And you can use it like
final client = YourGrpcClient(clientChannel, interceptors: [AuthenticationInterceptor()]);

Related

how to make singleton class with some initialization code?

I have tried the answers in here How do you build a Singleton in Dart?
but I can't achieve what I want. so basically I want to make a Shared Preference Service as a singleton class. currently my code is like this. this is just a regular class, not a singleton.
class SharedPreferenceService {
late SharedPreferences _prefs;
SharedPreferenceService() {
SharedPreferences.getInstance().then((value) => _prefs = value);
}
Future<void> setIntroPagesHaveBeenViewed() async {
await _prefs.setBool(SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
return _prefs.getBool(SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}
I need a singleton class, but when the instance is initialize for the first time, I also need to initialize _pref , so then I can access that _pref on the methods
Your problem is that initialization is asynchronous.
That means that the first time the singleton instance is accessed, that access needs to be asynchronous too (and so does any further access which happens before the initialization completes). However, the usage pattern of a singleton like this is such that you don't know which access is the first. So you have to make every access asynchronous.
Example:
class SharedPreferenceService {
static final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<void> setIntroPagesHaveBeenViewed() async {
await (await _prefs).setBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
return (await _prefs).getBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}
If all the methods are asynchronous anyway, that extra delay is not going to be a problem.
If you really, really only want to do that extra await if absolutely necessary,
you can cache the value, like you try to here:
class SharedPreferenceService {
static final Future<SharedPreferences> _prefsFuture = SharedPreferences.getInstance();
static SharedPreferences? _prefs;
Future<void> setIntroPagesHaveBeenViewed() async {
var prefs = _prefs ??= await _prefsFuture;
await _prefs.setBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED, true);
}
Future<bool> checkIfIntroPagesHaveBeenViewed() async {
var prefs = _prefs ??= await _prefsFuture;
return _prefs.getBool(
SharedPreferenceKey.INTRODUCTION_PAGES_HAVE_BEEN_VIEWED) ?? false;
}
}

dart await on constructor

What pattern should I use in this example to load and process some data. As value returns a value, it's not acceptable to have d as a Future. How can I get the constructor to wait until load has completed before continuing?
void main() {
var data = new Data(); // load data
print(data.value()); // data.d is still null
}
class Data {
String d;
Data() {
load();
}
Future<void> load() async {
d = await fn(); // some expensive function (e.g. loading a database)
}
String value() {
return d;
}
}
You cannot make a constructor asynchronous.
An asynchronous function needs to return a Future, and a constructor needs to return an instance of the class itself. Unless the class is a future, the constructor cannot be asynchronous (and even then, it's not really the same thing, and you can't use async/await).
So, if your class needs asynchronous set-up, you should provide the user with a static factory method instead of a constructor. I'd usually hide the constructor then.
class Data {
String _d;
Data._();
static Future<Data> create() async {
var data = Data._();
await data._load();
return data;
}
Future<void> _load() async {
_d = await fn();
}
String get value => _d;
}
As an alternative design, I wouldn't even put the load method on the class, just do the operation in the static factory method:
class Data {
String _d;
Data._(this._d);
static Future<Data> create() async => Data._(await fn());
String get value => _d;
}
Obviously other constraints might require that load has access to the object.

How to make a network request and return a json object in Dart

Making a network request is easy to Python and what makes it easy is that sync request.
In Dart, I can make a request like this:
HttpClient client = new HttpClient();
client.getUrl(Uri.parse("http://www.example.com/"))
.then((HttpClientRequest request) {
// Optionally set up headers...
// Optionally write to the request object...
// Then call close.
...
return request.close();
})
.then((HttpClientResponse response) {
// Process the response.
...
});
Obviously it's async request. In my opinion, above code can be reused many times. So I want to make a request and return a JSON object.
getResponse(String url) async {
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
HttpClientResponse response = await request.close();
String responseBody = await response.transform(utf8.decoder).join();
Map jsonResponse = jsonDecode(responseBody) as Map;
httpClient.close();
return jsonResponse;
}
As you see, the above method getResponse returns Future<dynamic>. So how can I call it and get the json value?
To get the dynamic from inside the Future you do one of the following:
// option 1 async method
MyAsyncMethod() async {
dynamic result = await getResponse("http://google.com");
if (result is Map) {
// process the data
}
}
// option 2 callback with .then()
MyNonAsyncMethod() {
getResponse("http://google.com").then ( (dynamic result) {
if (result is Map) {
// process the data
}
});
}
Note that your own async method can also return a Future<something> and be treated in the same two ways when called.
Where map is a nested Map<String, Dynamic> and result is an object of type of your creation that conforms to Json serialization interface (See this link).
To access Data in the map:
//given example json structure
var map = {
"myPropertyName":50,
"myArrayProperty":[
"anArrayEntry"
],
"mySubobject": {
"subObjectProperty":"someValue"
}
};
var myProperty = map["myPropertyName"]; // get a property value from the object
var myArrayEntry = map["myArrayProperty"][0]; // get first element of an array property
var mySubobjectPropertyValue = map["mySubobject"]["subObjectProperty"]; // get a property value from a subobject

Return string from HttpRequest

In Dart I can do:
await HttpRequest.getString(path)
and this will return a string.
I want to create a method that will do the same, but like this:
HttpRequest request = new HttpRequest();
request
..open('Get',getPath)
..setRequestHeader('Content-Type','application/json')
..send('');
...
return responseString;
I can do it using events and futures, but I would like to understand how to do it with async & await specifically.
Edit:
This is for the dart:html HttpRequest for browser.
Haven't tried but I guess this is what you're looking for
import 'dart:html';
import 'dart:async';
main() async {
print(await getString());
}
Future<String> getString() async {
String getPath = 'https://dartpad.dartlang.org/';
HttpRequest request = new HttpRequest();
request
..open('Get',getPath)
..setRequestHeader('Content-Type','application/json')
..send('');
// request.onReadyStateChange.listen(print);
await request.onLoadEnd.first;
return request.responseText;
}

Returning a String from an async

I want to return a String from an async function but I get a Future
What am I doing wrong;
Example
main() {
String s;
s = dummy("http://www.google.com");
}
String dummy(String s) {
String response;
response = readURL(s);
return response;
}
Future<String> readURL(String requestString) async {
String response = await http.read(requestString);
print(response);
return response;
}
Error:
type '_Future' is not a subtype of type 'String' of 'response'.
A function that's annotated with async will always return a Future.
so when you call readUrl(s) you can await its result.
To use await, the caller (here your main function) has to be marked as async. So the end result could look like this:
main() async {
String s = await dummy("http://www.google.com");
}
Future<String> dummy(String s) async {
String response = await readURL(s);
return (response);
}
Future<String> readURL(String requestString) async {
String response = await http.read(requestString);
print(response);
return(response);
}
The thing to notice here: If you use await in a function, it is now considered as function that returns a Future. So every function you convert to be async will now return a Future.
Here is the Simple Two way to get value from Function with return type Future<Type>
1- First way (best way, as you call this code from any file)
FutureFunctionName.then((val) {
val contains data
});
For example- (I am posting one from real example)
Future<String> getUserAgents() async {
String userAgent;
await FlutterUserAgent.init();
userAgent = FlutterUserAgent.webViewUserAgent;
return userAgent;
}
String userAgent;
getUserAgents().then((val) {
userAgent = val;
});
print(userAgent); // you will get output
2- Second way (use a global variable to get data)
String userAgent;
Future<void> getUserAgents() async {
await FlutterUserAgent.init();
userAgent = FlutterUserAgent.webViewUserAgent;
}
print(userAgent);

Resources