diff --git a/lib/openfoodfacts.dart b/lib/openfoodfacts.dart index 567c1b04a0..c8a7e7355a 100644 --- a/lib/openfoodfacts.dart +++ b/lib/openfoodfacts.dart @@ -49,6 +49,7 @@ export 'src/model/per_size.dart'; export 'src/model/product.dart'; export 'src/model/product_freshness.dart'; export 'src/model/product_type.dart'; +export 'src/model/product_type_filter.dart'; export 'src/model/product_image.dart'; export 'src/model/product_packaging.dart'; export 'src/model/product_result_field_answer.dart'; diff --git a/lib/src/model/old_product_result.dart b/lib/src/model/old_product_result.dart index 19adf68b31..500bd9c210 100644 --- a/lib/src/model/old_product_result.dart +++ b/lib/src/model/old_product_result.dart @@ -5,7 +5,8 @@ import 'product.dart'; part 'old_product_result.g.dart'; /// Product Result (old style). -// TODO(monsieurtanuki): get rid of it when OBF OPF OPFF support api v3 +// TODO: deprecated from 2024-11-28; remove when old enough +@Deprecated('Use getProductV3 and ProductResultV3 instead') @JsonSerializable() class OldProductResult extends JsonObject { final int? status; diff --git a/lib/src/model/product_type_filter.dart b/lib/src/model/product_type_filter.dart new file mode 100644 index 0000000000..1ba817ff05 --- /dev/null +++ b/lib/src/model/product_type_filter.dart @@ -0,0 +1,15 @@ +import 'off_tagged.dart'; +import 'product_type.dart'; + +/// Filter on product type for API get product queries. +class ProductTypeFilter implements OffTagged { + const ProductTypeFilter._(this.offTag); + + ProductTypeFilter(final ProductType productType) + : offTag = productType.offTag; + + static const ProductTypeFilter all = ProductTypeFilter._('all'); + + @override + final String offTag; +} diff --git a/lib/src/open_food_api_client.dart b/lib/src/open_food_api_client.dart index 067d33b92d..08d09b26c0 100644 --- a/lib/src/open_food_api_client.dart +++ b/lib/src/open_food_api_client.dart @@ -284,7 +284,8 @@ class OpenFoodAPIClient { /// Returns the product for the given barcode, with an old syntax. /// /// Temporarily needed for OBF, OPF and OPFF, that do not support api v3. - // TODO(monsieurtanuki): get rid of it when OBF OPF OPFF support api v3 + // TODO: deprecated from 2024-11-28; remove when old enough + @Deprecated('Use getProductV3 and ProductResultV3 instead') static Future getOldProduct( final ProductQueryConfiguration configuration, { final User? user, diff --git a/lib/src/utils/product_query_configurations.dart b/lib/src/utils/product_query_configurations.dart index 9d8676b1db..71248d4215 100644 --- a/lib/src/utils/product_query_configurations.dart +++ b/lib/src/utils/product_query_configurations.dart @@ -1,5 +1,6 @@ import 'package:http/http.dart'; +import '../model/product_type_filter.dart'; import '../model/user.dart'; import 'abstract_query_configuration.dart'; import 'http_helper.dart'; @@ -27,6 +28,9 @@ class ProductQueryConfiguration extends AbstractQueryConfiguration { /// The API version final ProductQueryVersion version; + /// Filter on a specific server. + final ProductTypeFilter? productTypeFilter; + /// See [AbstractQueryConfiguration.languages] for /// parameter's description. ProductQueryConfiguration( @@ -36,8 +40,18 @@ class ProductQueryConfiguration extends AbstractQueryConfiguration { super.languages, super.country, super.fields, + this.productTypeFilter, }); + @override + Map getParametersMap() { + final Map result = super.getParametersMap(); + if (productTypeFilter != null) { + result['product_type'] = productTypeFilter!.offTag; + } + return result; + } + /// If the provided [ProductQueryVersion] matches the API V3 requirements bool matchesV3() => version.matchesV3(); @@ -49,8 +63,8 @@ class ProductQueryConfiguration extends AbstractQueryConfiguration { final User? user, final UriProductHelper uriHelper, ) async { - if (version == ProductQueryVersion.v3) { - return await HttpHelper().doGetRequest( + if (matchesV3()) { + return HttpHelper().doGetRequest( uriHelper.getUri( path: getUriPath(), queryParameters: getParametersMap(), diff --git a/test/api_not_food_get_product_test.dart b/test/api_not_food_get_product_test.dart index c4bd916a56..2d70cda226 100644 --- a/test/api_not_food_get_product_test.dart +++ b/test/api_not_food_get_product_test.dart @@ -18,60 +18,90 @@ void main() { ProductType.beauty: '3600551054476', ProductType.product: '7898927451035', ProductType.petFood: '3564700266809', + ProductType.food: '7300400481588', }; - group('$OpenFoodAPIClient get not food products', () { - Future findProduct( - final String barcode, - final ProductType expectedProductType, - final ProductType serverProductType, - ) async { - final ProductQueryConfiguration configurations = - ProductQueryConfiguration( - barcode, - language: OpenFoodFactsLanguage.ENGLISH, - fields: [ - ProductField.BARCODE, - ProductField.PRODUCT_TYPE, - ], - version: ProductQueryVersion(2), - ); - await getProductTooManyRequestsManager.waitIfNeeded(); - final OldProductResult result = await OpenFoodAPIClient.getOldProduct( - configurations, - uriHelper: UriProductHelper( - domain: domains[serverProductType]!, - ), - ); - if (expectedProductType == serverProductType) { - expect(result.status, 1); - expect(result.barcode, barcode); - expect(result.product, isNotNull); - expect(result.product!.barcode, barcode); - expect(result.product!.productType, expectedProductType); - } else { - expect(result.status, 0); - expect(result.barcode, barcode); - expect(result.product, isNull); - } + Future findProduct( + final String barcode, + final ProductType expectedProductType, + final ProductType serverProductType, + final ProductTypeFilter? productTypeFilter, + ) async { + final ProductQueryConfiguration configurations = ProductQueryConfiguration( + barcode, + language: OpenFoodFactsLanguage.ENGLISH, + fields: [ + ProductField.BARCODE, + ProductField.PRODUCT_TYPE, + ], + version: ProductQueryVersion.v3, + productTypeFilter: productTypeFilter, + ); + await getProductTooManyRequestsManager.waitIfNeeded(); + final bool shouldSucceed = expectedProductType == serverProductType || + productTypeFilter == ProductTypeFilter.all || + productTypeFilter?.offTag == expectedProductType.offTag; + final ProductResultV3 result = await OpenFoodAPIClient.getProductV3( + configurations, + uriHelper: UriProductHelper( + domain: domains[serverProductType]!, + ), + ); + if (shouldSucceed) { + expect(result.status, ProductResultV3.statusSuccess); + expect(result.barcode, barcode); + expect(result.product, isNotNull); + expect(result.product!.barcode, barcode); + expect(result.product!.productType, expectedProductType); + } else { + expect(result.status, ProductResultV3.statusFailure); + expect(result.barcode, barcode); + expect(result.product, isNull); } + } - test('get OxF product', () async { - for (MapEntry item in barcodes.entries) { - final ProductType productType = item.key; - final String barcode = item.value; - for (final ProductType serverProductType in ProductType.values) { - await findProduct( - barcode, - productType, - serverProductType, - ); - } + Future checkProduct( + final ProductTypeFilter? filter, + ) async { + for (MapEntry item in barcodes.entries) { + final ProductType productType = item.key; + final String barcode = item.value; + for (final ProductType serverProductType in domains.keys) { + await findProduct( + barcode, + productType, + serverProductType, + filter, + ); } - }); - }, - timeout: Timeout( - // some tests can be slow here - Duration(seconds: 300), - )); + } + } + + group( + '$OpenFoodAPIClient get not food products v3', + () { + test( + 'get OxF product without filter', + () async => checkProduct(null), + ); + + test( + 'get OxF product with ALL filter', + () async => checkProduct(ProductTypeFilter.all), + ); + + test( + 'get OxF product with specific filter', + () async { + for (final ProductType productType in ProductType.values) { + await checkProduct(ProductTypeFilter(productType)); + } + }, + ); + }, + timeout: Timeout( + // some tests can be slow here + Duration(seconds: 300), + ), + ); } diff --git a/test/api_search_products_test.dart b/test/api_search_products_test.dart index 21e0620248..690808f494 100644 --- a/test/api_search_products_test.dart +++ b/test/api_search_products_test.dart @@ -47,7 +47,6 @@ void main() { '3608580823490', '3250391660995', '3760020506605', - '8722700202387', '3330720237330', '3535800940005', '20000691',