diff --git a/lib/model/ProductImage.dart b/lib/model/ProductImage.dart index 1cd43354ee..06961ca0b5 100644 --- a/lib/model/ProductImage.dart +++ b/lib/model/ProductImage.dart @@ -148,7 +148,7 @@ class ProductImage { String toString() => 'ProductImage(' '${field.value}' + - (size == null ? '' : ',size=${size.value}]') + + (size == null ? '' : ',size=${size.value}') + (language == null ? '' : ',language=${language.code}') + (angle == null ? '' : ',angle=${angle!.degreesClockwise}') + (url == null ? '' : ',url=$url') + diff --git a/lib/openfoodfacts.dart b/lib/openfoodfacts.dart index 2724ad96c3..6a512c56c7 100644 --- a/lib/openfoodfacts.dart +++ b/lib/openfoodfacts.dart @@ -459,10 +459,12 @@ class OpenFoodAPIClient { queryType: queryType, ); - Response response = await HttpHelper().doGetRequest(insightUri, - user: user, - userAgent: OpenFoodAPIConfiguration.userAgent, - queryType: queryType); + Response response = await HttpHelper().doGetRequest( + insightUri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); var result = InsightsResult.fromJson(json.decode(utf8.decode(response.bodyBytes))); @@ -479,10 +481,12 @@ class OpenFoodAPIClient { queryType: queryType, ); - Response response = await HttpHelper().doGetRequest(insightsUri, - user: user, - userAgent: OpenFoodAPIConfiguration.userAgent, - queryType: queryType); + Response response = await HttpHelper().doGetRequest( + insightsUri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); return InsightsResult.fromJson( json.decode(utf8.decode(response.bodyBytes))); @@ -510,10 +514,12 @@ class OpenFoodAPIClient { queryType: queryType, ); - Response response = await HttpHelper().doGetRequest(robotoffQuestionUri, - user: user, - userAgent: OpenFoodAPIConfiguration.userAgent, - queryType: queryType); + Response response = await HttpHelper().doGetRequest( + robotoffQuestionUri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); var result = RobotoffQuestionResult.fromJson( json.decode(utf8.decode(response.bodyBytes))); @@ -550,10 +556,12 @@ class OpenFoodAPIClient { queryType: queryType, ); - Response response = await HttpHelper().doGetRequest(robotoffQuestionUri, - user: user, - userAgent: OpenFoodAPIConfiguration.userAgent, - queryType: queryType); + Response response = await HttpHelper().doGetRequest( + robotoffQuestionUri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); var result = RobotoffQuestionResult.fromJson( json.decode(utf8.decode(response.bodyBytes))); @@ -618,10 +626,12 @@ class OpenFoodAPIClient { queryType: queryType, ); - Response response = await HttpHelper().doGetRequest(spellingCorrectionUri, - user: user, - userAgent: OpenFoodAPIConfiguration.userAgent, - queryType: queryType); + Response response = await HttpHelper().doGetRequest( + spellingCorrectionUri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); SpellingCorrection result = SpellingCorrection.fromJson( json.decode(utf8.decode(response.bodyBytes))); @@ -700,8 +710,11 @@ class OpenFoodAPIClient { path: '/cgi/auth.pl', queryType: queryType, ); - Response response = - await HttpHelper().doPostRequest(loginUri, user.toData(), user); + Response response = await HttpHelper().doPostRequest( + loginUri, + user.toData(), + user, + ); return response.statusCode == 200; } @@ -833,8 +846,11 @@ class OpenFoodAPIClient { }, ); try { - final Response response = await HttpHelper() - .doGetRequest(uri, userAgent: OpenFoodAPIConfiguration.userAgent); + final Response response = await HttpHelper().doGetRequest( + uri, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); if (response.statusCode != 200) { return null; } @@ -866,8 +882,11 @@ class OpenFoodAPIClient { ); try { - final Response response = await HttpHelper() - .doGetRequest(uri, userAgent: OpenFoodAPIConfiguration.userAgent); + final Response response = await HttpHelper().doGetRequest( + uri, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); if (response.statusCode != 200) { return KnowledgePanels.empty(); } @@ -900,6 +919,7 @@ class OpenFoodAPIClient { final Response response = await HttpHelper().doGetRequest( uri, userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, ); if (response.statusCode != 200) { throw Exception('Could not retrieve ordered nutrients!'); @@ -922,6 +942,7 @@ class OpenFoodAPIClient { required final OpenFoodFactsLanguage language, required final String imgid, required final ImageAngle angle, + required final User user, final QueryType? queryType, }) async => await _callProductImageCrop( @@ -929,6 +950,7 @@ class OpenFoodAPIClient { imageField: imageField, language: language, imgid: imgid, + user: user, extraParameters: { 'angle': angle.degreesClockwise, }, @@ -953,6 +975,7 @@ class OpenFoodAPIClient { required final int y1, required final int x2, required final int y2, + required final User user, final ImageAngle angle = ImageAngle.NOON, final QueryType? queryType, }) async => @@ -961,6 +984,7 @@ class OpenFoodAPIClient { imageField: imageField, language: language, imgid: imgid, + user: user, extraParameters: { 'x1': x1.toString(), 'y1': y1.toString(), @@ -981,6 +1005,7 @@ class OpenFoodAPIClient { required final OpenFoodFactsLanguage language, required final String imgid, required final Map extraParameters, + required final User user, final QueryType? queryType, }) async { final String id = '${imageField.value}_${language.code}'; @@ -996,8 +1021,12 @@ class OpenFoodAPIClient { queryParameters: queryParameters, ); - final Response response = await HttpHelper() - .doGetRequest(uri, userAgent: OpenFoodAPIConfiguration.userAgent); + final Response response = await HttpHelper().doGetRequest( + uri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); if (response.statusCode != 200) { throw Exception( 'Bad response (${response.statusCode}): ${response.body}'); @@ -1022,4 +1051,51 @@ class OpenFoodAPIClient { '/' + filename; } + + /// Unselect a product image. + /// + /// Typically, after that the openfoodfacts web page will _not_ show + /// the image as selected for this product x imagefield x language anymore. + /// Throws an exception if not successful. + /// Will work OK even when there was no previous selected product image. + static Future unselectProductImage({ + required final String barcode, + required final ImageField imageField, + required final OpenFoodFactsLanguage language, + required final User user, + final QueryType? queryType, + }) async { + final String id = '${imageField.value}_${language.code}'; + final Uri uri = UriHelper.getUri( + path: 'cgi/product_image_unselect.pl', + queryType: queryType, + queryParameters: {'code': barcode, 'id': id}, + ); + + final Response response = await HttpHelper().doGetRequest( + uri, + user: user, + userAgent: OpenFoodAPIConfiguration.userAgent, + queryType: queryType, + ); + if (response.statusCode != 200) { + throw Exception( + 'Bad response (${response.statusCode}): ${response.body}'); + } + final Map json = + jsonDecode(response.body) as Map; + final String status = json['status']; + if (status != 'status ok') { + throw Exception('Status not ok ($status)'); + } + final int statusCode = json['status_code']; + if (statusCode != 0) { + throw Exception('Status Code not ok ($statusCode)'); + } + final String imagefield = json['imagefield']; + if (imagefield != id) { + throw Exception( + 'Different imagefield: expected "$id", actual "$imageField"'); + } + } } diff --git a/test/api_addProductImage_test.dart b/test/api_addProductImage_test.dart index d28fb5d02f..5b0544ef64 100644 --- a/test/api_addProductImage_test.dart +++ b/test/api_addProductImage_test.dart @@ -7,6 +7,7 @@ import 'test_constants.dart'; void main() { OpenFoodAPIConfiguration.globalQueryType = QueryType.TEST; + const User user = TestConstants.TEST_USER; /// Common constants for several image operations const String barcode = '4250752200784'; @@ -83,7 +84,7 @@ void main() { imageUri: Uri.file('test/test_assets/front_de.jpg'), ); final Status status = await OpenFoodAPIClient.addProductImage( - TestConstants.TEST_USER, + user, image, ); @@ -99,7 +100,7 @@ void main() { imageUri: Uri.file('test/test_assets/ingredients_en.jpg'), ); Status status = await OpenFoodAPIClient.addProductImage( - TestConstants.TEST_USER, + user, image, ); @@ -113,7 +114,7 @@ void main() { ProductQueryConfiguration('7622210449283'); final ProductResult result = await OpenFoodAPIClient.getProduct( configurations, - user: TestConstants.TEST_USER, + user: user, ); expect(result.status, isNotNull); expect(result.product!.images, isNotEmpty); @@ -140,6 +141,7 @@ void main() { for (final ImageAngle angle in ImageAngle.values) { final String? newUrl = await OpenFoodAPIClient.setProductImageAngle( barcode: barcode, + user: user, imageField: imageField, language: language, imgid: imgid!, @@ -178,6 +180,7 @@ void main() { for (final ImageAngle angle in ImageAngle.values) { final String? newUrl = await OpenFoodAPIClient.setProductImageCrop( barcode: barcode, + user: user, imageField: imageField, language: language, imgid: imgid!, @@ -200,5 +203,34 @@ void main() { // this guy is rather slow Duration(seconds: 90), )); + + test('image unselect', () async { + const ImageField unselectedImageField = ImageField.INGREDIENTS; + await OpenFoodAPIClient.unselectProductImage( + barcode: barcode, + user: user, + imageField: unselectedImageField, + language: language, + ); + + final ProductResult productResult = await OpenFoodAPIClient.getProduct( + ProductQueryConfiguration( + barcode, + fields: [ProductField.SELECTED_IMAGE], + ), + ); + expect(productResult.product, isNotNull); + expect(productResult.product!.selectedImages, isNotNull); + for (final ProductImage productImage + in productResult.product!.selectedImages!) { + if (productImage.language == language) { + expect(productImage.field, isNot(unselectedImageField)); + } + } + }, + timeout: Timeout( + // this guy is rather slow + Duration(seconds: 90), + )); }); }