forked from openfoodfacts/openfoodfacts-dart
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: openfoodfacts#349 - added support for Events API
New files: * `api_events_test.dart`: Tests around Events API. * `BadgeBase.dart`: Events API: badge. * `BadgeBase.g.dart`: generated * `EventCreate.dart`: Events API: event create. * `EventCreate.g.dart`: generated * `events.dart`: Client calls of the Events API (Open Food Facts). * `EventsBase.dart`: Events API: event. * `EventsBase.g.dart`: generated * `LeaderboardEntry.dart`: Events API: leaderboard entry. * `LeaderboardEntry.g.dart`: generated Impacted files: * `JsonHelper.dart`: added a nullable timestamp conversion method. * `OpenFoodAPIConfiguration.dart`: added host entries for Events API. * `openfoodfacts.dart`: exported new Events related files. * `UriHelper.dart`: added Events related method `getEventsUri`.
- Loading branch information
1 parent
cf492c5
commit c7dc345
Showing
14 changed files
with
688 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
import 'dart:async'; | ||
import 'dart:convert'; | ||
|
||
import 'package:http/http.dart'; | ||
import 'package:openfoodfacts/model/BadgeBase.dart'; | ||
import 'package:openfoodfacts/model/EventCreate.dart'; | ||
import 'package:openfoodfacts/model/EventsBase.dart'; | ||
import 'package:openfoodfacts/model/LeaderboardEntry.dart'; | ||
import 'package:openfoodfacts/model/User.dart'; | ||
|
||
import 'utils/HttpHelper.dart'; | ||
import 'utils/QueryType.dart'; | ||
import 'utils/UriHelper.dart'; | ||
|
||
/// Client calls of the Events API (Open Food Facts). | ||
/// | ||
/// cf. https://events.openfoodfacts.net/docs | ||
class EventsAPIClient { | ||
EventsAPIClient._(); | ||
|
||
/// Returns all the [EventsBase], with optional filters and paging. | ||
static Future<List<EventsBase>> getEvents({ | ||
final String? userId, | ||
final String? deviceId, | ||
final int? skip, | ||
final int? limit, | ||
final QueryType? queryType, | ||
}) async { | ||
final Map<String, String> parameters = <String, String>{}; | ||
if (userId != null) { | ||
parameters['user_id'] = userId; | ||
} | ||
if (deviceId != null) { | ||
parameters['device_id'] = deviceId; | ||
} | ||
if (skip != null) { | ||
parameters['skip'] = skip.toString(); | ||
} | ||
if (limit != null) { | ||
parameters['limit'] = limit.toString(); | ||
} | ||
final Response response = await HttpHelper().doGetRequest( | ||
UriHelper.getEventsUri( | ||
path: '/events', | ||
queryParameters: parameters, | ||
queryType: queryType, | ||
), | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
final List<EventsBase> result = <EventsBase>[]; | ||
final List<dynamic> json = jsonDecode(response.body) as List<dynamic>; | ||
for (var element in json) { | ||
result.add(EventsBase.fromJson(element)); | ||
} | ||
return result; | ||
} | ||
|
||
/// Returns all the events counts, with optional filters. | ||
static Future<Map<String, int>> getEventsCount({ | ||
final String? userId, | ||
final String? deviceId, | ||
final QueryType? queryType, | ||
}) async { | ||
final Map<String, String> parameters = <String, String>{}; | ||
if (userId != null) { | ||
parameters['user_id'] = userId; | ||
} | ||
if (deviceId != null) { | ||
parameters['device_id'] = deviceId; | ||
} | ||
final Response response = await HttpHelper().doGetRequest( | ||
UriHelper.getEventsUri( | ||
path: '/events/count', | ||
queryParameters: parameters, | ||
queryType: queryType, | ||
), | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
final Map<String, int> result = <String, int>{}; | ||
final Map<String, dynamic> json = | ||
jsonDecode(response.body) as Map<String, dynamic>; | ||
for (final String key in json.keys) { | ||
result[key] = json[key] as int; | ||
} | ||
return result; | ||
} | ||
|
||
/// Returns the score, with optional filters. | ||
static Future<int> getScores({ | ||
final String? userId, | ||
final String? deviceId, | ||
final String? eventType, | ||
final QueryType? queryType, | ||
}) async { | ||
final Map<String, String> parameters = <String, String>{}; | ||
if (userId != null) { | ||
parameters['user_id'] = userId; | ||
} | ||
if (deviceId != null) { | ||
parameters['device_id'] = deviceId; | ||
} | ||
if (eventType != null) { | ||
parameters['event_type'] = eventType; | ||
} | ||
final Response response = await HttpHelper().doGetRequest( | ||
UriHelper.getEventsUri( | ||
path: '/scores', | ||
queryParameters: parameters, | ||
queryType: queryType, | ||
), | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
final Map<String, dynamic> json = | ||
jsonDecode(response.body) as Map<String, dynamic>; | ||
return json['score'] as int; | ||
} | ||
|
||
/// Returns all the [BadgeBase], with optional filters. | ||
static Future<List<BadgeBase>> getBadges({ | ||
final String? userId, | ||
final String? deviceId, | ||
final QueryType? queryType, | ||
}) async { | ||
final Map<String, String> parameters = <String, String>{}; | ||
if (userId != null) { | ||
parameters['user_id'] = userId; | ||
} | ||
if (deviceId != null) { | ||
parameters['device_id'] = deviceId; | ||
} | ||
final Response response = await HttpHelper().doGetRequest( | ||
UriHelper.getEventsUri( | ||
path: '/badges', | ||
queryParameters: parameters, | ||
queryType: queryType, | ||
), | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
final List<BadgeBase> result = <BadgeBase>[]; | ||
final List<dynamic> json = jsonDecode(response.body) as List<dynamic>; | ||
for (var element in json) { | ||
result.add(BadgeBase.fromJson(element)); | ||
} | ||
return result; | ||
} | ||
|
||
/// Returns all the [LeaderboardEntry], with optional filters. | ||
static Future<List<LeaderboardEntry>> getLeaderboard({ | ||
final String? eventType, | ||
final QueryType? queryType, | ||
}) async { | ||
final Map<String, String> parameters = <String, String>{}; | ||
if (eventType != null) { | ||
parameters['event_type'] = eventType; | ||
} | ||
final Response response = await HttpHelper().doGetRequest( | ||
UriHelper.getEventsUri( | ||
path: '/leaderboard', | ||
queryParameters: parameters, | ||
queryType: queryType, | ||
), | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
final List<LeaderboardEntry> result = <LeaderboardEntry>[]; | ||
final List<dynamic> json = jsonDecode(response.body) as List<dynamic>; | ||
for (var element in json) { | ||
result.add(LeaderboardEntry.fromJson(element)); | ||
} | ||
return result; | ||
} | ||
|
||
/// Adds an event. | ||
static Future<void> createEvent({ | ||
required final EventCreate eventCreate, | ||
required final User user, | ||
final QueryType? queryType, | ||
}) async { | ||
final Response response = await HttpHelper().doPostRequest( | ||
UriHelper.getEventsUri( | ||
path: '/events', | ||
queryType: queryType, | ||
), | ||
eventCreate.toData(), | ||
user, | ||
queryType: queryType, | ||
); | ||
_checkResponse(response); | ||
} | ||
|
||
/// Throws a detailed exception if relevant. Does nothing if [response] is OK. | ||
static void _checkResponse(final Response response) { | ||
if (response.statusCode != 200) { | ||
// cf. HTTPValidationError | ||
String? exception; | ||
try { | ||
final Map<String, dynamic> json = | ||
jsonDecode(response.body) as Map<String, dynamic>; | ||
exception = json['detail']; | ||
} catch (e) { | ||
// | ||
} | ||
if (exception != null) { | ||
throw Exception(exception); | ||
} | ||
// TODO(monsieurtanuki): have a look at ValidationError in https://events.openfoodfacts.org/docs | ||
throw Exception('Wrong status code: ${response.statusCode}'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import 'package:json_annotation/json_annotation.dart'; | ||
import '../interface/JsonObject.dart'; | ||
|
||
part 'BadgeBase.g.dart'; | ||
|
||
/// Events API: badge. | ||
@JsonSerializable() | ||
class BadgeBase extends JsonObject { | ||
@JsonKey(name: 'user_id') | ||
final String? userId; | ||
|
||
@JsonKey(name: 'badge_name') | ||
final String badgeName; | ||
|
||
@JsonKey() | ||
final int level; | ||
|
||
BadgeBase({ | ||
required this.badgeName, | ||
required this.level, | ||
this.userId, | ||
}); | ||
|
||
factory BadgeBase.fromJson(Map<String, dynamic> json) => | ||
_$BadgeBaseFromJson(json); | ||
|
||
@override | ||
Map<String, dynamic> toJson() => _$BadgeBaseToJson(this); | ||
|
||
@override | ||
String toString() => 'BadgeBase(badgeName: $badgeName' | ||
', level: $level' | ||
'${userId == null ? '' : ', userId: $userId'}' | ||
')'; | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import 'package:json_annotation/json_annotation.dart'; | ||
import 'package:openfoodfacts/utils/JsonHelper.dart'; | ||
import '../interface/JsonObject.dart'; | ||
|
||
part 'EventCreate.g.dart'; | ||
|
||
/// Events API: event create. | ||
@JsonSerializable() | ||
class EventCreate extends JsonObject { | ||
@JsonKey(name: 'event_type') | ||
final String eventType; | ||
|
||
@JsonKey(fromJson: JsonHelper.nullableStringTimestampToDate) | ||
final DateTime? timestamp; | ||
|
||
@JsonKey(name: 'user_id') | ||
final String? userId; | ||
|
||
@JsonKey() | ||
final String? barcode; | ||
|
||
@JsonKey() | ||
final int? points; | ||
|
||
@JsonKey(name: 'device_id') | ||
final String? deviceId; | ||
|
||
EventCreate({ | ||
required this.eventType, | ||
this.timestamp, | ||
this.userId, | ||
this.barcode, | ||
this.points, | ||
this.deviceId, | ||
}); | ||
|
||
factory EventCreate.fromJson(Map<String, dynamic> json) => | ||
_$EventCreateFromJson(json); | ||
|
||
@override | ||
Map<String, dynamic> toJson() => _$EventCreateToJson(this); | ||
|
||
@override | ||
String toString() => 'EventCreate(eventType: $eventType' | ||
'${timestamp == null ? '' : ', timestamp: $timestamp'}' | ||
'${userId == null ? '' : ', userId: $userId'}' | ||
'${barcode == null ? '' : ', barcode: $barcode'}' | ||
'${points == null ? '' : ', points: $points'}' | ||
'${deviceId == null ? '' : ', deviceId: $deviceId'}' | ||
')'; | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import 'package:json_annotation/json_annotation.dart'; | ||
import 'package:openfoodfacts/utils/JsonHelper.dart'; | ||
import '../interface/JsonObject.dart'; | ||
|
||
part 'EventsBase.g.dart'; | ||
|
||
/// Events API: event. | ||
@JsonSerializable() | ||
class EventsBase extends JsonObject { | ||
@JsonKey(name: 'event_type') | ||
final String eventType; | ||
|
||
@JsonKey(fromJson: JsonHelper.nullableStringTimestampToDate) | ||
final DateTime? timestamp; | ||
|
||
@JsonKey(name: 'user_id') | ||
final String? userId; | ||
|
||
@JsonKey() | ||
final String? barcode; | ||
|
||
@JsonKey() | ||
final int? points; | ||
|
||
EventsBase({ | ||
required this.eventType, | ||
this.timestamp, | ||
this.userId, | ||
this.barcode, | ||
this.points, | ||
}); | ||
|
||
factory EventsBase.fromJson(Map<String, dynamic> json) => | ||
_$EventsBaseFromJson(json); | ||
|
||
@override | ||
Map<String, dynamic> toJson() => _$EventsBaseToJson(this); | ||
|
||
@override | ||
String toString() => 'EventsBase(eventType: $eventType' | ||
'${timestamp == null ? '' : ', timestamp: $timestamp'}' | ||
'${userId == null ? '' : ', userId: $userId'}' | ||
'${barcode == null ? '' : ', barcode: $barcode'}' | ||
'${points == null ? '' : ', points: $points'}' | ||
')'; | ||
} |
Oops, something went wrong.