Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 82b060a

Browse files
Merge pull request #11 from CoderJava/remote-data-source-top-headlines-news
Create News Remote Data Source
2 parents 69db696 + 1c89a63 commit 82b060a

File tree

6 files changed

+160
-5
lines changed

6 files changed

+160
-5
lines changed

‎codemagic.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ workflows:
1414
events:
1515
- push
1616
branch_patterns:
17-
- pattern: '*dev*'
17+
- pattern: '*'
1818
include: true
1919
source: true
2020
scripts:

‎lib/config/base_url_config.dart‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class BaseUrlConfig {
2-
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003/v2';
3-
final String baseUrlProduction = 'https://newsapi.org/v2';
2+
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003';
3+
final String baseUrlProduction = 'https://newsapi.org';
44
}

‎lib/config/constant_config.dart‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class ConstantConfig {
2+
final String apiKeyNewsApi = '3f1bdb0927c94f8da5708f7a36f7d06f';
3+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:flutter_news_app/config/constant_config.dart';
3+
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
4+
import 'package:meta/meta.dart';
5+
6+
abstract class NewsRemoteDataSource {
7+
/// Calls the baseUrl:/v2/top-headlines?country=:country&apiKey=:apiKey endpoint
8+
///
9+
/// Throws a [DioError] for all error codes.
10+
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews();
11+
}
12+
13+
class NewsRemoteDataSourceImpl implements NewsRemoteDataSource {
14+
final Dio dio;
15+
final ConstantConfig constantConfig;
16+
17+
NewsRemoteDataSourceImpl({
18+
@required this.dio,
19+
@required this.constantConfig,
20+
});
21+
22+
@override
23+
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews() async {
24+
var response = await dio.get(
25+
'/v2/top-headlines',
26+
queryParameters: {
27+
'country': 'id',
28+
'apiKey': constantConfig.apiKeyNewsApi,
29+
},
30+
);
31+
if (response.statusCode == 200) {
32+
return TopHeadlinesNewsResponseModel.fromJson(response.data);
33+
} else {
34+
throw DioError();
35+
}
36+
}
37+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import 'dart:convert';
2+
3+
import 'package:dio/dio.dart';
4+
import 'package:flutter_news_app/config/base_url_config.dart';
5+
import 'package:flutter_news_app/config/constant_config.dart';
6+
import 'package:flutter_news_app/feature/data/datasource/news/news_remote_data_source.dart';
7+
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
8+
import 'package:flutter_test/flutter_test.dart';
9+
import 'package:matcher/matcher.dart';
10+
import 'package:mockito/mockito.dart';
11+
12+
import '../../../../fixture/fixture_reader.dart';
13+
14+
class MockDioAdapter extends Mock implements HttpClientAdapter {}
15+
16+
class MockDio extends Mock implements Dio {}
17+
18+
void main() {
19+
NewsRemoteDataSourceImpl newsRemoteDataSource;
20+
ConstantConfig constantConfig;
21+
MockDio mockDio;
22+
MockDioAdapter mockDioAdapter;
23+
24+
setUp(() {
25+
mockDio = MockDio();
26+
constantConfig = ConstantConfig();
27+
mockDioAdapter = MockDioAdapter();
28+
mockDio.httpClientAdapter = mockDioAdapter;
29+
mockDio.options = BaseOptions(
30+
baseUrl: BaseUrlConfig().baseUrlDevelopment,
31+
);
32+
newsRemoteDataSource = NewsRemoteDataSourceImpl(
33+
dio: mockDio,
34+
constantConfig: constantConfig,
35+
);
36+
});
37+
38+
group('getTopHeadlinesNews', () {
39+
final tTopHeadlinesNewsResponseModel = TopHeadlinesNewsResponseModel.fromJson(
40+
json.decode(
41+
fixture('top_headlines_news_response_model.json'),
42+
),
43+
);
44+
45+
void setUpMockDioSuccess() {
46+
final responsePayload = json.decode(fixture('top_headlines_news_response_model.json'));
47+
final response = Response(
48+
data: responsePayload,
49+
statusCode: 200,
50+
headers: Headers.fromMap({
51+
Headers.contentTypeHeader: [Headers.jsonContentType],
52+
}),
53+
);
54+
when(
55+
mockDio.get(
56+
any,
57+
queryParameters: anyNamed('queryParameters'),
58+
),
59+
).thenAnswer((_) async => response);
60+
}
61+
62+
test(
63+
'make sure there is a GET request to endpoint /v2/top-headlines?country=:country&apiKey=:apiKey',
64+
() async {
65+
// arrange
66+
setUpMockDioSuccess();
67+
68+
// act
69+
await newsRemoteDataSource.getTopHeadlinesNews();
70+
71+
// assert
72+
verify(
73+
mockDio.get(
74+
'/v2/top-headlines',
75+
queryParameters: {
76+
'country': 'id',
77+
'apiKey': constantConfig.apiKeyNewsApi,
78+
},
79+
),
80+
);
81+
},
82+
);
83+
84+
test(
85+
'make sure to return the TopHeadlinesNewsResponseModel object when the '
86+
'response code is successful from the endpoint',
87+
() async {
88+
// arrange
89+
setUpMockDioSuccess();
90+
91+
// act
92+
final result = await newsRemoteDataSource.getTopHeadlinesNews();
93+
94+
// assert
95+
expect(result, tTopHeadlinesNewsResponseModel);
96+
},
97+
);
98+
99+
test(
100+
'make sure to receive DioError when the response code fails from the endpoint',
101+
() async {
102+
// arrange
103+
final response = Response(
104+
data: 'Bad Request',
105+
statusCode: 400,
106+
);
107+
when(mockDio.get(any, queryParameters: anyNamed('queryParameters'))).thenAnswer((_) async => response);
108+
109+
// act
110+
final call = newsRemoteDataSource.getTopHeadlinesNews();
111+
112+
// assert
113+
expect(() => call, throwsA(TypeMatcher<DioError>()));
114+
},
115+
);
116+
});
117+
}

‎test/fixture/fixture_reader.dart‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import 'dart:io';
22

3-
final baseUrlTest = 'https://bengkelrobot.net:8003/v2';
4-
53
String fixture(String name) {
64
var currentDirectory = Directory.current.toString().replaceAll('\'', '');
75
var lastDirectory = currentDirectory.split('/')[currentDirectory.split('/').length - 1];

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /