Skip to content

Instantly share code, notes, and snippets.

@nguthiru
Last active March 27, 2023 07:37
Show Gist options
  • Save nguthiru/3ef2b8b63f96f51bb9e6b7a4f0514c6f to your computer and use it in GitHub Desktop.
Save nguthiru/3ef2b8b63f96f51bb9e6b7a4f0514c6f to your computer and use it in GitHub Desktop.
Dart HTTP base package with caching
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:soul_date/services/network.dart';
import '../constants/constants.dart';
class DataCache {
static final DataCache _instance = DataCache._internal();
factory DataCache() => _instance;
DataCache._internal();
static const int _cacheDuration = 1 * 60 * 60;
// 1 hour in seconds
Future<bool> isValid(String cacheKey) async {
var preferences = await SharedPreferences.getInstance();
var cacheTime = preferences.getInt(cacheKey + '_time');
return cacheTime != null &&
(DateTime.now().millisecondsSinceEpoch - cacheTime) >
_cacheDuration * 1000;
}
Future<String?> getJson(String cacheKey) async {
var preferences = await SharedPreferences.getInstance();
var cacheTime = preferences.getInt(cacheKey + '_time');
// preferences.remove(cacheKey);
if (cacheTime != null &&
(DateTime.now().millisecondsSinceEpoch - cacheTime) >
_cacheDuration * 1000) {
return null;
} else {
return preferences.getString(cacheKey);
}
}
Future<bool> setJson(String key, String str) async {
var preferences = await SharedPreferences.getInstance();
preferences.setInt(key + '_time', DateTime.now().millisecondsSinceEpoch);
return preferences.setString(key, str);
}
}
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:soul_date/services/cache.dart';
import '../constants/constants.dart';
class HttpClient {
String _formatEndpoint(String endpoint, Map<String, String>? parameters) {
if (parameters == null || parameters == {}) {
return endpoint;
} else {
return Uri.parse(endpoint)
.replace(queryParameters: parameters)
.toString();
}
}
Future<String?> getToken() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getString('token');
}
Future<http.Response> get(String endpoint,
{Map<String, String>? parameters,
Map<String, String>? headers,
bool cache = false,
bool useToken = true}) async {
String? token = await getToken();
final _headers = headers ?? {};
if (useToken && token != null) {
_headers['Authorization'] = "Token $token";
}
var endpointUrl = _formatEndpoint(endpoint, parameters);
if (cache) {
var cachedResponse =
await DataCache().getJson(_formatEndpoint(endpoint, parameters));
if (cachedResponse != null) {
return http.Response(cachedResponse, 200);
}
}
http.Response res = await http.get(
Uri.parse(
endpointUrl,
),
headers: _headers);
if (cache) {
DataCache().setJson(endpointUrl, res.body);
}
return res;
}
Future<http.Response> post(String endpoint,
{required Map<String, dynamic>? body,
Map<String, String>? headers,
Map<String, String>? parameters,
Map<String, String>? headersAdd,
Object? bodyRaw,
XFile? file,
bool useToken = true}) async {
String? token = await getToken();
late Map<String, String> _headers;
if (headers != null) {
_headers = headers;
} else {
_headers = {};
}
if (headersAdd != null) {
_headers.addAll(headersAdd);
}
if (token != null && useToken) {
_headers['Authorization'] = "Token $token";
}
if (file == null) {
http.Response res = await http.post(
Uri.parse(
_formatEndpoint(endpoint, parameters),
),
body: bodyRaw ?? body,
headers: _headers);
return res;
} else {
var request = http.MultipartRequest("PUT", Uri.parse(endpoint));
request.files.add(await http.MultipartFile.fromPath('image', file.path));
var body_pass = body!.cast<String, String>();
request.fields.addAll(body_pass);
request.headers.addAll(_headers);
var streamedResponse = await request.send();
return http.Response.fromStream(streamedResponse);
}
}
Future<http.Response> patch(String endpoint, Map body) async {
String? token = await getToken();
http.Response res = await http.patch(Uri.parse(endpoint),
body: body, headers: {'Authorization': 'Token $token'});
return res;
}
Future<http.Response> delete(String endpoint) async {
String? token = await getToken();
var res = await http.delete(Uri.parse(endpoint),
headers: {'Authorization': 'Token $token'});
return res;
}
Future<http.Response> put(String endpoint,
{Map<String, dynamic>? body,
Object? bodyRaw,
Map<String, String>? parameters,
Map<String, String>? headers,
bool useToken = true}) async {
String? token = await getToken();
late Map<String, String> _headers;
if (headers != null) {
_headers = headers;
} else {
_headers = {};
}
if (useToken && token != null) {
_headers['Authorization'] = "Token $token";
}
http.Response res = await http.put(
Uri.parse(
_formatEndpoint(endpoint, parameters),
),
headers: _headers,
body: body ?? bodyRaw);
return res;
}
}
class SpotifyClient {
HttpClient client = HttpClient();
String basicAuth(String username, String password) {
return 'Basic ' + base64Encode(utf8.encode('$username:$password'));
}
String get _clientSecret {
return "edbd307b1b064e91a8974698bb9b0a8f";
}
Future<Map?> getTokens(String code) async {
http.Response res =
await client.post(spotifyTokenEndpoint, useToken: false, body: {
'code': code,
'clientId': clientId,
'grant_type': 'authorization_code',
'redirect_uri': 'souldate:/'
}, headers: {
'Content-type': 'application/x-www-form-urlencoded',
'Authorization': basicAuth(clientId, _clientSecret)
});
if (res.statusCode <= 210) {
return jsonDecode(res.body);
} else {
log(res.body, name: "SPOTIFY ERROR");
}
return null;
}
Future<bool> refreshAccessToken() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var spotRefresh = preferences.getString("spotify_refreshtoken");
http.Response res =
await client.post(spotifyTokenEndpoint, useToken: false, body: {
'grant_type': 'refresh_token',
'refresh_token': spotRefresh!,
'redirect_uri': 'souldate:/',
}, headers: {
'Content-type': 'application/x-www-form-urlencoded',
'Authorization': basicAuth(clientId, _clientSecret)
});
if (res.statusCode <= 210) {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setString(
"spotify_accesstoken", json.decode(res.body)['access_token']);
// accessToken = json.decode(res.body)['access_token'];
return true;
} else {
log(res.body, name: "REFRESH TOKEN ERROR");
return false;
}
}
Future<http.Response> get(String endpoint,
{Map<String, String>? parameters,
Map<String, String>? headers,
bool cache = true}) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var accessToken = preferences.getString("spotify_accesstoken")!;
http.Response res = await client.get(endpoint,
parameters: parameters,
headers: headers ??
{
'Authorization': "Bearer $accessToken",
"Content-Type": "application/json"
},
useToken: false);
if (res.statusCode == 401 &&
json.decode(res.body)['error']['message'] ==
"The access token expired") {
await refreshAccessToken();
get(endpoint, parameters: parameters);
}
return res;
}
Future<http.Response> post(
String endpoint, {
required Map<String, String> body,
Map<String, String>? headers,
Map<String, String>? parameters,
}) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var accessToken = preferences.getString("spotify_accesstoken")!;
headers ??= {'Authorization': "Bearer $accessToken"};
http.Response res = await client.post(endpoint,
body: body, parameters: parameters, headers: headers, useToken: false);
if (res.statusCode == 401 &&
json.decode(res.body)['error']['message'] ==
"The access token expired") {
await refreshAccessToken();
post(endpoint, body: body, headers: headers, parameters: parameters);
}
return res;
}
Future<http.Response> put(
String endpoint, {
Map<String, String>? body,
Object? bodyRaw,
Map<String, String>? headers,
Map<String, String>? parameters,
}) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var accessToken = preferences.getString("spotify_accesstoken")!;
// var refreshToken = preferences.getString("spotify_refreshtoken")!;
headers ??= {
'Authorization': "Bearer $accessToken",
"Content-Type": "application/json"
};
http.Response res = await client.put(endpoint,
body: body,
bodyRaw: bodyRaw,
headers: headers,
parameters: parameters,
useToken: false);
if (res.statusCode == 401 &&
json.decode(res.body)['error']['message'] ==
"The access token expired") {
await refreshAccessToken();
put(
endpoint,
body: body,
bodyRaw: bodyRaw,
parameters: parameters,
headers: headers,
);
}
return res;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment