diff --git a/lib/model/TaxonomyCategory.dart b/lib/model/TaxonomyCategory.dart index 1fce109aaa..735ba4f089 100644 --- a/lib/model/TaxonomyCategory.dart +++ b/lib/model/TaxonomyCategory.dart @@ -348,6 +348,21 @@ class TaxonomyCategoryQueryConfiguration extends TaxonomyQueryConfiguration< additionalParameters: additionalParameters, ); + TaxonomyCategoryQueryConfiguration.roots({ + List? languages = const [], + String? cc, + bool includeChildren = false, + List fields = const [], + List additionalParameters = const [], + }) : super.roots( + TagType.CATEGORIES, + languages: languages, + cc: cc, + includeChildren: includeChildren, + fields: fields, + additionalParameters: additionalParameters, + ); + @override Map convertResults(dynamic jsonData) { if (jsonData is! Map) { diff --git a/lib/model/TaxonomyIngredient.dart b/lib/model/TaxonomyIngredient.dart index 7317eb2886..7e1afb2bef 100644 --- a/lib/model/TaxonomyIngredient.dart +++ b/lib/model/TaxonomyIngredient.dart @@ -485,6 +485,21 @@ class TaxonomyIngredientQueryConfiguration extends TaxonomyQueryConfiguration< additionalParameters: additionalParameters, ); + TaxonomyIngredientQueryConfiguration.roots({ + List? languages = const [], + String? cc, + List fields = const [], + List additionalParameters = const [], + bool includeChildren = false, + }) : super.roots( + TagType.INGREDIENTS, + languages: languages, + cc: cc, + includeChildren: includeChildren, + fields: fields, + additionalParameters: additionalParameters, + ); + @override Map convertResults(dynamic jsonData) { if (jsonData is! Map) { diff --git a/lib/model/TaxonomyLabel.dart b/lib/model/TaxonomyLabel.dart index 353c3487d4..d5a51b48c0 100644 --- a/lib/model/TaxonomyLabel.dart +++ b/lib/model/TaxonomyLabel.dart @@ -292,6 +292,20 @@ class TaxonomyLabelQueryConfiguration additionalParameters: additionalParameters, ); + TaxonomyLabelQueryConfiguration.roots({ + List? languages = const [], + String? cc, + List fields = const [], + List additionalParameters = const [], + }) : super.roots( + TagType.LABELS, + languages: languages, + cc: cc, + includeChildren: false, + fields: fields, + additionalParameters: additionalParameters, + ); + @override Map convertResults(dynamic jsonData) { if (jsonData is! Map) { diff --git a/lib/utils/TaxonomyQueryConfiguration.dart b/lib/utils/TaxonomyQueryConfiguration.dart index f9d37ed73f..078702eb24 100644 --- a/lib/utils/TaxonomyQueryConfiguration.dart +++ b/lib/utils/TaxonomyQueryConfiguration.dart @@ -35,10 +35,13 @@ abstract class TaxonomyQueryConfiguration 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. @@ -47,6 +50,9 @@ abstract class TaxonomyQueryConfiguration[]; + + TaxonomyQueryConfiguration.roots( + this.tagType, { + List? languages, + this.cc, + this.includeChildren = false, + this.fields = const [], + this.additionalParameters = const [], + }) : _isRootConfiguration = true, + tags = const [], + languages = languages ?? OpenFoodAPIConfiguration.globalLanguages ?? const []; @@ -67,7 +87,13 @@ abstract class TaxonomyQueryConfiguration 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) { diff --git a/test/api_getTaxonomyCategories_test.dart b/test/api_getTaxonomyCategories_test.dart index 83eb074ea0..bbc60b16f1 100644 --- a/test/api_getTaxonomyCategories_test.dart +++ b/test/api_getTaxonomyCategories_test.dart @@ -17,6 +17,62 @@ void main() { }); group('OpenFoodAPIClient getTaxonomyCategories', () { + test('get root categories', () async { + final String tag = 'en:plain-crepes'; + Map expectedResponse = { + 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? 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 expectedResponse = { diff --git a/test/api_getTaxonomyIngredients_test.dart b/test/api_getTaxonomyIngredients_test.dart index ec91ca675e..82842f861e 100644 --- a/test/api_getTaxonomyIngredients_test.dart +++ b/test/api_getTaxonomyIngredients_test.dart @@ -18,6 +18,61 @@ void main() { }); group('OpenFoodAPIClient getTaxonomyIngredients', () { + test('get an ingredient roots', () async { + final String tag = 'en:edamame'; + Map expectedResponse = { + 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? 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 expectedResponse = { diff --git a/test/api_getTaxonomyLabels_test.dart b/test/api_getTaxonomyLabels_test.dart index 6b21923178..6984f9fd03 100644 --- a/test/api_getTaxonomyLabels_test.dart +++ b/test/api_getTaxonomyLabels_test.dart @@ -33,7 +33,10 @@ void main() { 'name': { 'en': 'Vegetarian', 'fr': 'V\u00e9g\u00e9tarien', - } + }, + 'parents': [ + 'en:food', + ], } }; @@ -43,6 +46,69 @@ void main() { }); group('OpenFoodAPIClient getTaxonomyLabels', () { + test('get label roots', () async { + final Map expectedResponse = { + '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? 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 =