Skip to content

Commit

Permalink
Add queries to get the roots from the server (openfoodfacts#287)
Browse files Browse the repository at this point in the history
This adds parameters to the Taxonomy queries that have hierarchies (Category, Ingredient, Label) that allow fetching of the root entries of the taxonomy (the entries without parents).
  • Loading branch information
gspencergoog authored Nov 1, 2021
1 parent a4d3c36 commit ad4d829
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 5 deletions.
15 changes: 15 additions & 0 deletions lib/model/TaxonomyCategory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,21 @@ class TaxonomyCategoryQueryConfiguration extends TaxonomyQueryConfiguration<
additionalParameters: additionalParameters,
);

TaxonomyCategoryQueryConfiguration.roots({
List<OpenFoodFactsLanguage>? languages = const [],
String? cc,
bool includeChildren = false,
List<TaxonomyCategoryField> fields = const [],
List<Parameter> additionalParameters = const [],
}) : super.roots(
TagType.CATEGORIES,
languages: languages,
cc: cc,
includeChildren: includeChildren,
fields: fields,
additionalParameters: additionalParameters,
);

@override
Map<String, TaxonomyCategory> convertResults(dynamic jsonData) {
if (jsonData is! Map<String, dynamic>) {
Expand Down
15 changes: 15 additions & 0 deletions lib/model/TaxonomyIngredient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,21 @@ class TaxonomyIngredientQueryConfiguration extends TaxonomyQueryConfiguration<
additionalParameters: additionalParameters,
);

TaxonomyIngredientQueryConfiguration.roots({
List<OpenFoodFactsLanguage>? languages = const [],
String? cc,
List<TaxonomyIngredientField> fields = const [],
List<Parameter> additionalParameters = const [],
bool includeChildren = false,
}) : super.roots(
TagType.INGREDIENTS,
languages: languages,
cc: cc,
includeChildren: includeChildren,
fields: fields,
additionalParameters: additionalParameters,
);

@override
Map<String, TaxonomyIngredient> convertResults(dynamic jsonData) {
if (jsonData is! Map<String, dynamic>) {
Expand Down
14 changes: 14 additions & 0 deletions lib/model/TaxonomyLabel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,20 @@ class TaxonomyLabelQueryConfiguration
additionalParameters: additionalParameters,
);

TaxonomyLabelQueryConfiguration.roots({
List<OpenFoodFactsLanguage>? languages = const [],
String? cc,
List<TaxonomyLabelField> fields = const [],
List<Parameter> additionalParameters = const [],
}) : super.roots(
TagType.LABELS,
languages: languages,
cc: cc,
includeChildren: false,
fields: fields,
additionalParameters: additionalParameters,
);

@override
Map<String, TaxonomyLabel> convertResults(dynamic jsonData) {
if (jsonData is! Map<String, dynamic>) {
Expand Down
34 changes: 30 additions & 4 deletions lib/utils/TaxonomyQueryConfiguration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ abstract class TaxonomyQueryConfiguration<T extends JsonObject,

/// The tags to request.
///
/// If empty, no results will be returned.
/// If empty, no results will be returned. This field is ignored if
/// [TaxonomyQueryConfiguration.roots] is used to create the configuration.
final List<String> tags;

/// The desired taxonomy fields to retrieve. If empty, retrieve all fields.
/// If true, include the children of the requested tag in the results.
///
/// Defaults to false.
final bool includeChildren;

/// Additional parameters to add the to query.
Expand All @@ -47,6 +50,9 @@ abstract class TaxonomyQueryConfiguration<T extends JsonObject,
/// The type of tags that this query should request a taxonomy for.
final TagType tagType;

// True if created via TaxonomyQueryConfiguration.roots.
final bool _isRootConfiguration;

/// Allows subclasses to create a [TaxonomyQueryConfiguration] from the
/// supplied parameters.
TaxonomyQueryConfiguration(
Expand All @@ -57,7 +63,21 @@ abstract class TaxonomyQueryConfiguration<T extends JsonObject,
this.includeChildren = false,
this.fields = const [],
this.additionalParameters = const [],
}) : languages = languages ??
}) : _isRootConfiguration = false,
languages = languages ??
OpenFoodAPIConfiguration.globalLanguages ??
const <OpenFoodFactsLanguage>[];

TaxonomyQueryConfiguration.roots(
this.tagType, {
List<OpenFoodFactsLanguage>? languages,
this.cc,
this.includeChildren = false,
this.fields = const [],
this.additionalParameters = const [],
}) : _isRootConfiguration = true,
tags = const <String>[],
languages = languages ??
OpenFoodAPIConfiguration.globalLanguages ??
const <OpenFoodFactsLanguage>[];

Expand All @@ -67,7 +87,13 @@ abstract class TaxonomyQueryConfiguration<T extends JsonObject,
final Map<String, String> result = {};

result['type'] = tagType.key;
result['tags'] = tags.join(',');
if (_isRootConfiguration) {
result['include_root_entries'] = '1';
} else {
if (tags.isNotEmpty) {
result['tags'] = tags.join(',');
}
}
result['include_children'] = includeChildren ? '1' : '0';

if (languages.isNotEmpty) {
Expand Down
56 changes: 56 additions & 0 deletions test/api_getTaxonomyCategories_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,62 @@ void main() {
});

group('OpenFoodAPIClient getTaxonomyCategories', () {
test('get root categories', () async {
final String tag = 'en:plain-crepes';
Map<String, dynamic> expectedResponse = <String, dynamic>{
tag: {
'agribalyse_food_code': {'en': '23800'},
'children': ['en:refrigerated-plain-crepes'],
'name': {'en': 'Plain crepes', 'fr': 'Crêpe nature'},
}
};
TaxonomyCategoryQueryConfiguration configuration =
TaxonomyCategoryQueryConfiguration.roots(
fields: [
TaxonomyCategoryField.NAME,
TaxonomyCategoryField.CHILDREN,
TaxonomyCategoryField.PARENTS,
TaxonomyCategoryField.AGRIBALYSE_FOOD_CODE
],
languages: [
OpenFoodFactsLanguage.ENGLISH,
OpenFoodFactsLanguage.FRENCH,
],
);
httpHelper.setResponse(configuration.getUri(),
response: expectedResponse);

Map<String, TaxonomyCategory>? categories =
await OpenFoodAPIClient.getTaxonomyCategories(
configuration,
user: TestConstants.TEST_USER,
);
expect(categories, isNotNull);
expect(categories!.length, equals(1));
TaxonomyCategory crepes = categories[tag]!;
expect(
crepes.agribalyseFoodCode![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag]
[TaxonomyCategoryField.AGRIBALYSE_FOOD_CODE.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(
crepes.children!.length,
equals(expectedResponse[tag][TaxonomyCategoryField.CHILDREN.key]
.length));
expect(
crepes.children!.first,
equals(
expectedResponse[tag][TaxonomyCategoryField.CHILDREN.key].first));
expect(crepes.parents, isNull);
expect(
crepes.name![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag][TaxonomyCategoryField.NAME.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(
crepes.name![OpenFoodFactsLanguage.FRENCH]!,
equals(expectedResponse[tag][TaxonomyCategoryField.NAME.key]
[OpenFoodFactsLanguage.FRENCH.code]));
});
test('get a category', () async {
final String tag = 'en:plain-crepes';
Map<String, dynamic> expectedResponse = <String, dynamic>{
Expand Down
55 changes: 55 additions & 0 deletions test/api_getTaxonomyIngredients_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,61 @@ void main() {
});

group('OpenFoodAPIClient getTaxonomyIngredients', () {
test('get an ingredient roots', () async {
final String tag = 'en:edamame';
Map<String, dynamic> expectedResponse = <String, dynamic>{
tag: {
'children': ['en:mukimame'],
'name': {'en': 'Edamame', 'fr': 'Edamame'},
'wikidata': {'en': 'Q1377879'}
}
};
TaxonomyIngredientQueryConfiguration configuration =
TaxonomyIngredientQueryConfiguration.roots(
fields: [
TaxonomyIngredientField.NAME,
TaxonomyIngredientField.CHILDREN,
TaxonomyIngredientField.PARENTS,
TaxonomyIngredientField.WIKIDATA,
],
languages: [
OpenFoodFactsLanguage.ENGLISH,
OpenFoodFactsLanguage.FRENCH,
],
);
httpHelper.setResponse(configuration.getUri(),
response: expectedResponse);

Map<String, TaxonomyIngredient>? ingredients =
await OpenFoodAPIClient.getTaxonomyIngredients(
configuration,
user: TestConstants.TEST_USER,
);
expect(ingredients, isNotNull);
expect(ingredients!.length, equals(1));
TaxonomyIngredient edamame = ingredients[tag]!;
expect(
edamame.wikidata![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag][TaxonomyIngredientField.WIKIDATA.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(
edamame.children!.length,
equals(expectedResponse[tag][TaxonomyIngredientField.CHILDREN.key]
.length));
expect(
edamame.children!.first,
equals(expectedResponse[tag][TaxonomyIngredientField.CHILDREN.key]
.first));
expect(edamame.parents, isNull);
expect(
edamame.name![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag][TaxonomyIngredientField.NAME.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(
edamame.name![OpenFoodFactsLanguage.FRENCH]!,
equals(expectedResponse[tag][TaxonomyIngredientField.NAME.key]
[OpenFoodFactsLanguage.FRENCH.code]));
});
test('get an ingredient', () async {
final String tag = 'en:edamame';
Map<String, dynamic> expectedResponse = <String, dynamic>{
Expand Down
68 changes: 67 additions & 1 deletion test/api_getTaxonomyLabels_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ void main() {
'name': {
'en': 'Vegetarian',
'fr': 'V\u00e9g\u00e9tarien',
}
},
'parents': [
'en:food',
],
}
};

Expand All @@ -43,6 +46,69 @@ void main() {
});

group('OpenFoodAPIClient getTaxonomyLabels', () {
test('get label roots', () async {
final Map<String, dynamic> expectedResponse = <String, dynamic>{
'en:vegetarian': {
'description': {
'fr':
'Le v\u00e9g\u00e9tarisme est une pratique alimentaire qui exclut la consommation de chair animale.',
'en':
'Vegetarianism is the practice of abstaining from the consumption of meat, and may also include abstention from by-products of animal slaughter.',
},
'wikidata': {'en': 'Q638022'},
'children': [
'da:dansk-vegetarisk-forening',
'en:european-vegetarian-union',
'en:green-dot-india',
'en:vegan',
'en:vege-project',
'en:vegetarian-society',
'it:icea-bio-vegetariano',
'it:icea-vegetariano',
],
'name': {
'en': 'Vegetarian',
'fr': 'V\u00e9g\u00e9tarien',
},
}
};
final String tag = 'en:vegetarian';
TaxonomyLabelQueryConfiguration configuration =
TaxonomyLabelQueryConfiguration.roots(
fields: [
TaxonomyLabelField.NAME,
TaxonomyLabelField.WIKIDATA,
],
languages: [
OpenFoodFactsLanguage.ENGLISH,
OpenFoodFactsLanguage.FRENCH,
],
);
httpHelper.setResponse(configuration.getUri(),
response: expectedResponse);

Map<String, TaxonomyLabel>? labels =
await OpenFoodAPIClient.getTaxonomyLabels(
configuration,
user: TestConstants.TEST_USER,
);
expect(labels, isNotNull);
expect(labels!.length, equals(1));
TaxonomyLabel label = labels[tag]!;
expect(
label.name![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag][TaxonomyLabelField.NAME.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(
label.name![OpenFoodFactsLanguage.FRENCH]!,
equals(expectedResponse[tag][TaxonomyLabelField.NAME.key]
[OpenFoodFactsLanguage.FRENCH.code]));
expect(
label.wikidata![OpenFoodFactsLanguage.ENGLISH]!,
equals(expectedResponse[tag][TaxonomyLabelField.WIKIDATA.key]
[OpenFoodFactsLanguage.ENGLISH.code]));
expect(label.parents, isNull);
});
test('get a label', () async {
final String tag = 'en:vegetarian';
TaxonomyLabelQueryConfiguration configuration =
Expand Down

0 comments on commit ad4d829

Please sign in to comment.