Skip to content

Commit

Permalink
feat: add jsonSerialize in user-model, checkUnauthorized exception in…
Browse files Browse the repository at this point in the history
… httpUtils, move getStorageCache to Utils
  • Loading branch information
cevheri committed Nov 9, 2024
1 parent c9de665 commit 6f47a79
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 67 deletions.
2 changes: 1 addition & 1 deletion lib/configuration/environment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class _Config {
static const API = "API";

static Map<String, dynamic> devConstants = {
API: "http://localhost/assets/mock",
API: "mock/",
};

static Map<String, dynamic> prodConstants = {
Expand Down
69 changes: 41 additions & 28 deletions lib/data/http_utils.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert' show Encoding, json, jsonDecode, utf8;
import 'dart:convert' show Encoding, json, utf8;
import 'dart:developer';
import 'dart:io';

import 'package:dart_json_mapper/dart_json_mapper.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';

import '../configuration/allowed_paths.dart';
import '../configuration/environment.dart';
import '../main/main_local.dart';
import '../utils/app_constants.dart';
import '../utils/storage.dart';
import 'app_api_exception.dart';

class MyHttpOverrides extends HttpOverrides {
Expand Down Expand Up @@ -74,7 +73,13 @@ class HttpUtils {
return headerParameters;
}

static Future<Response> postRequest<T>(String endpoint, T body, {Map<String, String>? headers}) async {
static void checkUnauthorizedAccess(String endpoint, http.Response response) {
if (response.statusCode == 401) {
throw UnauthorizedException(response.body.toString());
}
}

static Future<http.Response> postRequest<T>(String endpoint, T body, {Map<String, String>? headers}) async {
/// if isMock is true, return mock data instead of making a request
if (!ProfileConstants.isProduction) return await mockRequest('POST', endpoint);

Expand All @@ -95,7 +100,7 @@ class HttpUtils {
messageBody = body as String;
}

Response? response;
http.Response? response;
try {
final url = Uri.parse('${ProfileConstants.api}$endpoint');

Expand All @@ -107,31 +112,35 @@ class HttpUtils {
encoding: Encoding.getByName('utf-8'),
)
.timeout(Duration(seconds: timeout));

checkUnauthorizedAccess(endpoint, response);
} on SocketException catch (se) {
debugPrint("Socket Exception: $se");
throw FetchDataException('No Internet connection');
} on TimeoutException catch (toe) {
debugPrint("Timeout Exception: $toe");
throw FetchDataException('Request timeout');
}

return response;
}

static Future<String> getRequest(String endpoint) async {
static Future<http.Response> getRequest(String endpoint) async {
debugPrint("GET Request Method start : ${ProfileConstants.api}$endpoint");

/// if isMock is true, return mock data instead of making a request
if (!ProfileConstants.isProduction) return (await mockRequest('GET', endpoint)).body.toString();
if (!ProfileConstants.isProduction) return (await mockRequest('GET', endpoint));

var headers = await HttpUtils.headers();
try {
var response = await http.get(Uri.parse('${ProfileConstants.api}$endpoint'), headers: headers).timeout(Duration(seconds: timeout));
if (response.statusCode == 401) {
throw UnauthorizedException(response.body.toString());
}
var result = decodeUTF8(response.body.toString());
debugPrint(" GET Request Method end : ${ProfileConstants.api}$endpoint");
return result;
var response = await http
.get(
Uri.parse('${ProfileConstants.api}$endpoint'),
headers: headers,
)
.timeout(Duration(seconds: timeout));
checkUnauthorizedAccess(endpoint, response);
return response;
} on SocketException {
throw FetchDataException('No Internet connection');
} on TimeoutException {
Expand Down Expand Up @@ -161,7 +170,7 @@ class HttpUtils {
// }
// }

static Future<Response> putRequest<T>(String endpoint, T body) async {
static Future<http.Response> putRequest<T>(String endpoint, T body) async {
if (!ProfileConstants.isProduction) return await mockRequest('PUT', endpoint);
var headers = await HttpUtils.headers();
final String json = JsonMapper.serialize(
Expand All @@ -173,11 +182,12 @@ class HttpUtils {
ignoreUnknownTypes: true,
),
);
Response response;
http.Response response;
try {
response = await http
.put(Uri.parse('${ProfileConstants.api}$endpoint'), headers: headers, body: json, encoding: Encoding.getByName('utf-8'))
.timeout(Duration(seconds: timeout));
checkUnauthorizedAccess(endpoint, response);
} on SocketException {
throw FetchDataException('No Internet connection');
} on TimeoutException {
Expand All @@ -186,7 +196,7 @@ class HttpUtils {
return response;
}

static Future<Response> patchRequest<T>(String endpoint, T body) async {
static Future<http.Response> patchRequest<T>(String endpoint, T body) async {
if (!ProfileConstants.isProduction) return await mockRequest('PATCH', endpoint);
var headers = await HttpUtils.headers();
final String json = JsonMapper.serialize(
Expand All @@ -198,11 +208,12 @@ class HttpUtils {
ignoreUnknownTypes: true,
),
);
Response response;
http.Response response;
try {
response = await http
.patch(Uri.parse('${ProfileConstants.api}$endpoint'), headers: headers, body: json, encoding: Encoding.getByName('utf-8'))
.timeout(Duration(seconds: timeout));
checkUnauthorizedAccess(endpoint, response);
} on SocketException {
throw FetchDataException('No Internet connection');
} on TimeoutException {
Expand All @@ -211,11 +222,13 @@ class HttpUtils {
return response;
}

static Future<Response> deleteRequest(String endpoint) async {
static Future<http.Response> deleteRequest(String endpoint) async {
if (!ProfileConstants.isProduction) return await mockRequest('DELETE', endpoint);
var headers = await HttpUtils.headers();
try {
return await http.delete(Uri.parse('${ProfileConstants.api}$endpoint'), headers: headers).timeout(Duration(seconds: timeout));
var response = await http.delete(Uri.parse('${ProfileConstants.api}$endpoint'), headers: headers).timeout(Duration(seconds: timeout));
checkUnauthorizedAccess(endpoint, response);
return response;
} on SocketException {
throw FetchDataException('No Internet connection');
} on TimeoutException {
Expand Down Expand Up @@ -244,38 +257,38 @@ class HttpUtils {
// }
// }

static Future<Response> mockRequest(String httpMethod, String endpoint) async {
static Future<http.Response> mockRequest(String httpMethod, String endpoint) async {
debugPrint("Mock request: $httpMethod $endpoint");

var headers = await HttpUtils.headers();
if (!allowedPaths.contains(endpoint)) {
if (headers['Authorization'] == null) {
return Future.value(Response("Unauthorized", HttpStatus.unauthorized));
return Future.value(http.Response("Unauthorized", HttpStatus.unauthorized));
}
}

String responseBody = "OK";
int httpStatusCode = HttpStatus.ok;
Future<Response> response = Future.value(Response("", httpStatusCode));
Future<http.Response> response = Future.value(http.Response("", httpStatusCode));
switch (httpMethod) {
case 'POST':
httpStatusCode = HttpStatus.created;
break;
case 'DELETE':
httpStatusCode = HttpStatus.noContent;
return Future.value(Response(responseBody, httpStatusCode));
return Future.value(http.Response(responseBody, httpStatusCode));
case 'GET':
case 'PUT':
default:
httpStatusCode = HttpStatus.ok;
}

try {
String path = 'mock/';
String path = ProfileConstants.api;
String fileName = "$httpMethod${endpoint.replaceAll("/", "_")}.json";
String mockDataPath = path + fileName;
responseBody = await rootBundle.loadString(mockDataPath);
response = Future.value(Response(responseBody, httpStatusCode));
response = Future.value(http.Response(responseBody, httpStatusCode));
debugPrint("Mock data loaded from $responseBody");
} catch (e) {
debugPrint("Error loading mock data httpMethod:$httpMethod, endpoint:$endpoint. error: $e");
Expand All @@ -288,7 +301,7 @@ class HttpUtils {
var responseJson = json.decode(responseBody);
responseJson['login'] = username;
responseJson['authorities'] = ['ROLE_${username.toUpperCase()}'];
response = Future.value(Response(json.encode(responseJson), httpStatusCode));
response = Future.value(http.Response(json.encode(responseJson), httpStatusCode));
} catch (e) {
debugPrint("There is no response body to update with username");
}
Expand Down
34 changes: 21 additions & 13 deletions lib/data/models/user.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:convert';

import 'package:dart_json_mapper/dart_json_mapper.dart';
import 'package:equatable/equatable.dart';

Expand All @@ -7,7 +9,7 @@ import 'package:equatable/equatable.dart';
@jsonSerializable
class User extends Equatable {
@JsonProperty(name: 'id')
final int? id;
final String? id;

@JsonProperty(name: 'login')
final String? login;
Expand Down Expand Up @@ -45,8 +47,7 @@ class User extends Equatable {
// @JsonProperty(name: 'phoneNumber')
// final String? phoneNumber;


User({
const User({
this.id,
this.login,
this.firstName,
Expand All @@ -63,7 +64,7 @@ class User extends Equatable {
});

User copyWith({
int? id,
String? id,
String? login,
String? firstName,
String? lastName,
Expand Down Expand Up @@ -94,6 +95,22 @@ class User extends Equatable {
);
}

static User? fromJson(Map<String, dynamic> json) {
var result = JsonMapper.deserialize<User>(json);
if (result == null) {
return null;
}
return result;
}

static User? fromJsonString(String json) {
var result = JsonMapper.deserialize<User>(jsonDecode(json));
if (result == null) {
return null;
}
return result;
}

@override
List<Object?> get props => [
id,
Expand All @@ -113,13 +130,4 @@ class User extends Equatable {

@override
bool get stringify => true;

// static fromJson(JsonMap json){
// return User(
// id:
// login: json['login'],
// firstName: json['firstName'],
// lastName: json['lastName'],
//
// }
}
18 changes: 10 additions & 8 deletions lib/data/repository/account_repository.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'dart:io';

import 'package:dart_json_mapper/dart_json_mapper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc_advance/utils/storage.dart';
import 'package:http/http.dart';

import '../http_utils.dart';
import '../models/change_password.dart';
Expand All @@ -14,10 +12,11 @@ class AccountRepository {

final String _resource = "account";

Future<Response> register(User newUser) async {
Future<User?> register(User newUser) async {
debugPrint("register repository start");
final registerRequest = await HttpUtils.postRequest<User>("/register", newUser);
return registerRequest;
final httpResponse = await HttpUtils.postRequest<User>("/register", newUser);
var response = HttpUtils.decodeUTF8(httpResponse.body.toString());
return User.fromJsonString(response);
}

Future<int> changePassword(PasswordChangeDTO passwordChangeDTO) async {
Expand All @@ -39,10 +38,13 @@ class AccountRepository {

Future<User> getAccount() async {
debugPrint("getAccount repository start");
final response = await HttpUtils.getRequest("/$_resource");
final httpResponse = await HttpUtils.getRequest("/$_resource");

var result = JsonMapper.deserialize<User>(response)!;
saveStorage(role: result.authorities?[0] ?? "");
var response = HttpUtils.decodeUTF8(httpResponse.body.toString());
debugPrint(" GET Request Method result : $response");

var result = User.fromJsonString(response)!;
saveStorage(roles: result.authorities);
debugPrint("getAccount successful - response : $response}");
return result;
}
Expand Down
4 changes: 0 additions & 4 deletions lib/main/main_local.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ import 'main_local.mapper.g.dart' show initializeJsonMapper;
// flutter pub run intl_utils:generate
/// main entry point of local computer development
Map<String, dynamic> getStorageCache = {};

Future<void> loadStorageData() async {
getStorageCache = await getStorage();
}

void main() async {
ProfileConstants.setEnvironment(Environment.DEV);
Expand Down
4 changes: 2 additions & 2 deletions lib/main/main_local.mapper.g.dart

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions lib/main/main_prod.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import 'main_local.mapper.g.dart' show initializeJsonMapper;

/// main entry point of local computer development
Map<String, dynamic> getStorageCache = {};

Future<void> loadStorageData() async {
getStorageCache = await getStorage();
}

void main() async {
ProfileConstants.setEnvironment(Environment.PROD);
Expand Down
4 changes: 2 additions & 2 deletions lib/main/main_prod.mapper.g.dart

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions lib/presentation/screen/register/bloc/register_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import '../../../../data/models/user.dart';
import '../../../../data/repository/account_repository.dart';

part 'register_event.dart';

part 'register_state.dart';

class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
Expand All @@ -28,9 +27,9 @@ class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
emit(RegisterInitialState());
try {
var resultStatusCode = await _accountRepository.register(event.createUset);
resultStatusCode.statusCode == 201 ? emit(RegisterCompletedState()) : emit(const RegisterErrorState(message: "Reset Password Error"));
emit(RegisterCompletedState());
} catch (e) {
emit(const RegisterErrorState(message: "Reset Password Error"));
emit(const RegisterErrorState(message: "Register Error"));
rethrow;
}
}
Expand Down
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -878,10 +878,10 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.3.3"
shared_preferences_android:
dependency: transitive
description:
Expand Down

0 comments on commit 6f47a79

Please sign in to comment.