Skip to content

RestauranTour Superformula Test - Guilherme Fonseca #17

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

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
04f844d
feat: packages update
fonsecguilherme Sep 13, 2024
9568673
feat: git ignore update
fonsecguilherme Sep 13, 2024
847451e
feat: mock yelp data to avoid exceed api limit
fonsecguilherme Sep 13, 2024
5ef0402
feat: files update
fonsecguilherme Sep 13, 2024
9ff580d
Merge pull request #1 from fonsecguilherme/develop
fonsecguilherme Sep 13, 2024
43323b8
WIP: restaurants and favorites cubit implementation
fonsecguilherme Sep 13, 2024
bfad1d7
feat: common widgets
fonsecguilherme Sep 13, 2024
252ac80
feat: app pages implementation
fonsecguilherme Sep 13, 2024
c36e121
feat: changed file location
fonsecguilherme Sep 13, 2024
a171f26
feat: service locator implementation
fonsecguilherme Sep 13, 2024
3911f25
feat: http client implementation
fonsecguilherme Sep 13, 2024
2756e83
WIP: repository implementation
fonsecguilherme Sep 13, 2024
ecd119f
feat: pubspec update
fonsecguilherme Sep 13, 2024
a09634d
feat: app setup
fonsecguilherme Sep 14, 2024
14ba42c
fix: file formatting
fonsecguilherme Sep 14, 2024
5761e1e
feat: home page update
fonsecguilherme Sep 15, 2024
c4200ba
fix: cubit validation update
fonsecguilherme Sep 15, 2024
7eda0c1
feat: pubspec update
fonsecguilherme Sep 15, 2024
a1cf3cf
feat: page and widgets tests
fonsecguilherme Sep 16, 2024
bc579d7
feat: cubit tests
fonsecguilherme Sep 16, 2024
6a7a25f
feat: repository abstraction and implementations
fonsecguilherme Sep 16, 2024
f6779ed
feat: pages and widgets changes
fonsecguilherme Sep 16, 2024
b2f07ab
fix: moved files to core folder
fonsecguilherme Sep 16, 2024
6f49588
feat: flavors implementation
fonsecguilherme Sep 16, 2024
40e5caf
fix: cubit changes to use dartz package
fonsecguilherme Sep 16, 2024
ce6b344
fix: changed import path
fonsecguilherme Sep 16, 2024
5865238
fix: fixed bug where http post call was not working properly
fonsecguilherme Sep 16, 2024
c3283df
feat: now app call api according to what flavor is selected
fonsecguilherme Sep 16, 2024
fb12502
Merge pull request #2 from fonsecguilherme/develop
fonsecguilherme Sep 16, 2024
46faae3
feat: implemented data persistence
fonsecguilherme Sep 16, 2024
eb60d0f
fix: fixed broken tests
fonsecguilherme Sep 16, 2024
29cf8c4
feat: repository tests
fonsecguilherme Sep 16, 2024
b505e1b
feat: git ignore update
fonsecguilherme Sep 16, 2024
a0e34f4
feat: removed envied package depencies and using dart define to secur…
fonsecguilherme Sep 16, 2024
9d393e7
Merge pull request #3 from fonsecguilherme/develop
fonsecguilherme Sep 16, 2024
67e91dd
Update README.md
fonsecguilherme Sep 16, 2024
ff23f19
Update README.md
fonsecguilherme Sep 16, 2024
2cd1aba
Update README.md
fonsecguilherme Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,11 @@ app.*.map.json
/android/app/release

# fvm
.fvm/flutter_sdk
.fvm/flutter_sdk

# exclude all .env files from source control
*.env
*env.g.dart

#exclude api-key file
*api-keys.json
15 changes: 13 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,20 @@
"version": "0.2.0",
"configurations": [
{
"name": "app",
"name": "App Dev",
"request": "launch",
"type": "dart"
"type": "dart",
"program": "lib/main_dev.dart"
},
{
"name": "App Prod",
"request": "launch",
"type": "dart",
"program": "lib/main_prod.dart",
"args": [
"--dart-define-from-file",
"api-keys.json"
]
}
]
}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dart.flutterSdkPath": ".fvm/flutter_sdk",
"dart.flutterSdkPath": "/opt/homebrew/Caskroom/flutter/3.22.2/flutter",
"search.exclude": {
"**/.fvm": true
},
Expand Down
273 changes: 145 additions & 128 deletions README.md

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions lib/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:restaurant_tour/core/dependency_injection/service_locator.dart';
import 'package:restaurant_tour/data/repositories/yelp_repository.dart';
import 'package:restaurant_tour/data/shared_services.dart';
import 'package:restaurant_tour/view/cubit/favorite/favorite.dart';
import 'package:restaurant_tour/view/pages/home/home_page.dart';

import 'view/cubit/restaurants/restaurants.dart';

class App extends StatelessWidget {
const App({super.key});

@override
Widget build(BuildContext context) => MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiBlocProvider(
providers: [
BlocProvider<RestaurantsCubit>(
create: (context) =>
RestaurantsCubit(dependency<YelpRepository>()),
),
BlocProvider<FavoriteCubit>(
create: (context) => FavoriteCubit(
sharedServices: dependency<SharedServices>(),
),
),
],
child: const HomePage(),
),
);
}
35 changes: 35 additions & 0 deletions lib/core/dependency_injection/service_locator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:restaurant_tour/core/http_service/http_client.dart';
import 'package:restaurant_tour/data/shared_services.dart';

import '../../data/repositories/yelp_dev_repository.dart';
import '../../data/repositories/yelp_prod_repository.dart';
import '../../data/repositories/yelp_repository.dart';
import '../flavors.dart';

final dependency = GetIt.instance;

void setupLocator({required Flavor flavor}) {
dependency.registerLazySingleton<http.Client>(
() => http.Client(),
);

dependency.registerLazySingleton<IHttpClient>(
() => HttpClient(dependency<http.Client>()),
);

dependency.registerLazySingleton<SharedServices>(
() => SharedServices(),
);

dependency.registerLazySingleton<YelpRepository>(
() {
if (flavor == Flavor.prod) {
return YelpProdRepository(client: dependency<IHttpClient>());
}

return YelpDevRepository(client: dependency<IHttpClient>());
},
);
}
1 change: 1 addition & 0 deletions lib/core/flavors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enum Flavor { prod, dev }
72 changes: 72 additions & 0 deletions lib/core/http_service/http_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'package:http/http.dart' as http;

abstract class IHttpClient {
Future<HttpResponse> post(
String url, {
Map<String, String>? headers,
Object? body,
});

Future<HttpResponse> get(
String url, {
Map<String, String>? headers,
});
}

class HttpResponse {
final String body;
final int statusCode;

const HttpResponse({
required this.body,
required this.statusCode,
});
}

class HttpClient implements IHttpClient {
final http.Client client;

const HttpClient(this.client);

@override
Future<HttpResponse> post(
String url, {
Map<String, String>? headers,
Object? body,
}) async {
try {
final response = await client.post(
Uri.parse(url),
headers: headers,
body: body,
);

return HttpResponse(
body: response.body,
statusCode: response.statusCode,
);
} catch (e) {
throw Exception('An error happened: $e');
}
}

@override
Future<HttpResponse> get(
String url, {
Map<String, String>? headers,
}) async {
try {
final response = await client.get(
Uri.parse(url),
headers: headers,
);

return HttpResponse(
body: response.body,
statusCode: response.statusCode,
);
} catch (e) {
throw Exception('An error happened: $e');
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions lib/data/repositories/yelp_dev_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'dart:convert';

import 'package:dartz/dartz.dart';
import 'package:restaurant_tour/core/http_service/http_client.dart';
import 'package:restaurant_tour/data/repositories/yelp_repository.dart';

import '../models/restaurant.dart';

class YelpDevRepository implements YelpRepository {
final IHttpClient client;

YelpDevRepository({required this.client});

@override
Future<Option<RestaurantQueryResult>> getRestaurants({int offset = 0}) async {
const baseUrl =
'https://raw.githubusercontent.com/fonsecguilherme/sf_flutter_test/master/restaurants.json';

try {
final response = await client.get(baseUrl);

if (response.statusCode == 200) {
return Some(
RestaurantQueryResult.fromJson(
jsonDecode(response.body)['data']['search'],
),
);
} else {
print('Failed to load restaurants: ${response.statusCode}');
return const None();
}
} catch (e) {
print('Error fetching restaurants: $e');
return const None();
}
}
}
50 changes: 50 additions & 0 deletions lib/data/repositories/yelp_prod_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:convert';

import 'package:dartz/dartz.dart';
import 'package:restaurant_tour/core/http_service/http_client.dart';
import 'package:restaurant_tour/data/repositories/yelp_repository.dart';

import '../../core/query.dart';
import '../models/restaurant.dart';

class YelpProdRepository implements YelpRepository {
final IHttpClient client;

YelpProdRepository({required this.client});

@override
Future<Option<RestaurantQueryResult>> getRestaurants({int offset = 0}) async {
const yelpApiKey = String.fromEnvironment('YELP_KEY');
if (yelpApiKey.isEmpty) {
throw AssertionError('YELP KEY IS NOT SET');
}

final headers = {
'Authorization': 'Bearer $yelpApiKey',
'Content-Type': 'application/graphql',
};

const baseUrl = 'https://api.yelp.com/v3/graphql';

try {
final response = await client.post(
baseUrl,
headers: headers,
body: query(offset),
);
if (response.statusCode == 200) {
return Some(
RestaurantQueryResult.fromJson(
jsonDecode(response.body)['data']['search'],
),
);
} else {
print('Failed to load restaurants: ${response.statusCode}');
return const None();
}
} catch (e) {
print('Error fetching restaurants: $e');
return const None();
}
}
}
7 changes: 7 additions & 0 deletions lib/data/repositories/yelp_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:dartz/dartz.dart';

import '../models/restaurant.dart';

abstract class YelpRepository {
Future<Option<RestaurantQueryResult>> getRestaurants({int offset = 0});
}
38 changes: 38 additions & 0 deletions lib/data/shared_services.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

import 'models/restaurant.dart';

class SharedServices {
static SharedPreferences? _preferences;

static Future<void> _getPreferences() async {
_preferences ??= await SharedPreferences.getInstance();
}

Future<void> saveListString(
String key,
List<Restaurant> restaurantList,
) async {
await _getPreferences();

List<String> encodedList = restaurantList
.map((restaurant) => jsonEncode(restaurant.toJson()))
.toList();

await _preferences!.setStringList(key, encodedList);
}

Future<List<Restaurant>> getListString(String key) async {
await _getPreferences();

final jsonList = _preferences!.getStringList(key) ?? [];

return jsonList.map((e) => Restaurant.fromJson(json.decode(e))).toList();
}
}

class SharedPreferencesKeys {
static String savedRestaurants = 'savedRestaurants';
}
Loading