Skip to content

Commit

Permalink
feat: 995 - support of the new "product type" filter for "get product" (
Browse files Browse the repository at this point in the history
#1004)

* feat: 995 - support of the new "product type" filter for "get product"

New file:
* `product_type_filter.dart`: Filter on product type for API get product queries.

Impacted files
* `api_not_food_get_product_test.dart`: now testing only in v3 with all product type filters
* `old_product_result.dart`: deprecated class `OldProductResult`
* `open_food_api_client.dart`: deprecated method `getOldProduct`
* `openfoodfacts.dart`: export new file `product_type_filter.dart`
* `product_query_configurations.dart`: "product_type_filter" as new optional "get product" parameter; minor refactoring

* minor fix

cf. openfoodfacts/openfoodfacts-server#11069
  • Loading branch information
monsieurtanuki authored Nov 29, 2024
1 parent 7a9c761 commit 032a547
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 56 deletions.
1 change: 1 addition & 0 deletions lib/openfoodfacts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
3 changes: 2 additions & 1 deletion lib/src/model/old_product_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions lib/src/model/product_type_filter.dart
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 2 additions & 1 deletion lib/src/open_food_api_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<OldProductResult> getOldProduct(
final ProductQueryConfiguration configuration, {
final User? user,
Expand Down
18 changes: 16 additions & 2 deletions lib/src/utils/product_query_configurations.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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(
Expand All @@ -36,8 +40,18 @@ class ProductQueryConfiguration extends AbstractQueryConfiguration {
super.languages,
super.country,
super.fields,
this.productTypeFilter,
});

@override
Map<String, String> getParametersMap() {
final Map<String, String> 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();

Expand All @@ -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(),
Expand Down
132 changes: 81 additions & 51 deletions test/api_not_food_get_product_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> 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<void> 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<ProductType, String> 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<void> checkProduct(
final ProductTypeFilter? filter,
) async {
for (MapEntry<ProductType, String> 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),
),
);
}
1 change: 0 additions & 1 deletion test/api_search_products_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ void main() {
'3608580823490',
'3250391660995',
'3760020506605',
'8722700202387',
'3330720237330',
'3535800940005',
'20000691',
Expand Down

0 comments on commit 032a547

Please sign in to comment.