Skip to content

Commit

Permalink
feat: 659 - support of product field "packagingsComplete" (#664)
Browse files Browse the repository at this point in the history
Impacted files:
* `api_save_product_v3_test.dart`: added a save/read test about new product field `packagingsComplete`
* `json_helper.dart`: added helper methods about bool to/from 0/1 conversion.
* `open_food_api_client.dart`: added a way to save new product field `packagingsComplete`
* `product.dart`: added field `packagingsComplete`
* `product.g.dart`: generated
* `product_fields.dart`: added field `PACKAGINGS_COMPLETE`
  • Loading branch information
monsieurtanuki committed Dec 31, 2022
1 parent c5335b7 commit ddd61a3
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 6 deletions.
8 changes: 8 additions & 0 deletions lib/src/model/product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ class Product extends JsonObject {
@JsonKey(name: 'packagings', includeIfNull: false)
List<ProductPackaging>? packagings;

/// Is the "packagings" complete?
@JsonKey(
name: 'packagings_complete',
toJson: JsonHelper.boolToJSON,
fromJson: JsonHelper.boolFromJSON,
)
bool? packagingsComplete;

@JsonKey(name: 'packaging_tags', includeIfNull: false)
List<String>? packagingTags;
@JsonKey(
Expand Down
4 changes: 4 additions & 0 deletions lib/src/model/product.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 13 additions & 5 deletions lib/src/open_food_api_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,26 @@ class OpenFoodAPIClient {
final User user,
final String barcode, {
final List<ProductPackaging>? packagings,
final bool? packagingsComplete,
final QueryType? queryType,
final OpenFoodFactsCountry? country,
final OpenFoodFactsLanguage? language,
}) async {
final Map<String, dynamic> parameterMap = <String, dynamic>{};
parameterMap.addAll(user.toData());
if (packagings == null) {
// For the moment it's the only purpose of this method: saving packagings.
throw Exception('packagings cannot be null');
if (packagings == null && packagingsComplete == null) {
// For the moment there are limited fields concerned.
throw Exception('At least one V3 field must be populated.');
}
const String productTag = 'product';
parameterMap[productTag] = {};
if (packagings != null) {
parameterMap[productTag][ProductField.PACKAGINGS.offTag] = packagings;
}
if (packagingsComplete != null) {
parameterMap[productTag][ProductField.PACKAGINGS_COMPLETE.offTag] =
packagingsComplete;
}
parameterMap['product'] = {};
parameterMap['product']['packagings'] = packagings;
if (language != null) {
parameterMap['lc'] = language.offTag;
parameterMap['tags_lc'] = language.offTag;
Expand Down
21 changes: 21 additions & 0 deletions lib/src/utils/json_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ class JsonHelper {
static const String _checkboxOnValue = 'on';
static const String _checkboxOffValue = '';

/// Returns a bool from ''/'on' conversion.
static bool? checkboxFromJSON(dynamic jsonValue) {
if (jsonValue == null) {
return null;
Expand All @@ -225,6 +226,7 @@ class JsonHelper {
jsonValue.trim().toLowerCase() == _checkboxOnValue;
}

/// Returns a bool to ''/'on' conversion.
static String? checkboxToJSON(dynamic value) {
if (value == null) {
return null;
Expand All @@ -235,4 +237,23 @@ class JsonHelper {
return _checkboxOffValue;
}
}

/// Returns a bool from 0/1 conversion.
static bool? boolFromJSON(dynamic jsonValue) {
if (jsonValue == null) {
return null;
}
return jsonValue == 1;
}

/// Returns a bool to 0/1 conversion.
static int? boolToJSON(dynamic value) {
if (value == null) {
return null;
}
if (value == true || value == 1) {
return 1;
}
return 0;
}
}
1 change: 1 addition & 0 deletions lib/src/utils/product_fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum ProductField implements OffTagged {
@Deprecated('Use packagingS field instead')
PACKAGING(offTag: 'packaging'),
PACKAGINGS(offTag: 'packagings'),
PACKAGINGS_COMPLETE(offTag: 'packagings_complete'),
PACKAGING_TAGS(offTag: 'packaging_tags'),
PACKAGING_TEXT_IN_LANGUAGES(offTag: 'packaging_text_'),
PACKAGING_TEXT_ALL_LANGUAGES(offTag: 'packaging_text_languages'),
Expand Down
49 changes: 48 additions & 1 deletion test/api_save_product_v3_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ void main() {

group('$OpenFoodAPIClient save product V3', () {
const String barcode = '12345678';
const OpenFoodFactsLanguage language = OpenFoodFactsLanguage.FRENCH;
const OpenFoodFactsCountry country = OpenFoodFactsCountry.FRANCE;

test('save packagings with unknown recycling', () async {
// Here we put an unknown recycling label, and we expect related warnings.
Expand All @@ -31,7 +33,7 @@ void main() {
TestConstants.TEST_USER,
barcode,
queryType: QueryType.TEST,
country: OpenFoodFactsCountry.FRANCE,
country: country,
language: language,
packagings: inputPackagings,
);
Expand Down Expand Up @@ -69,6 +71,51 @@ void main() {
expect(answer.field!.id, 'recycling');
expect(answer.field!.value, '${language.offTag}:$unknownRecycling');
});

test('save packagings_complete', () async {
final List<bool> values = [false, true, false];
for (final bool value in values) {
final ProductResultV3 writeStatus =
await OpenFoodAPIClient.temporarySaveProductV3(
TestConstants.TEST_USER,
barcode,
queryType: QueryType.TEST,
country: country,
language: language,
packagingsComplete: value,
);

expect(writeStatus.status, ProductResultV3.statusSuccess);
expect(writeStatus.errors, isEmpty);
expect(writeStatus.result, isNull); // result is null for UPDATE queries
expect(writeStatus.barcode, barcode);
expect(writeStatus.product, isNotNull);
expect(writeStatus.product!.packagingsComplete, value);

// checking again...
final ProductResultV3 readStatus = await OpenFoodAPIClient.getProductV3(
ProductQueryConfiguration(
barcode,
language: language,
country: country,
version: ProductQueryVersion.v3,
fields: [
ProductField.BARCODE,
ProductField.PACKAGINGS_COMPLETE,
],
),
user: TestConstants.TEST_USER,
);

expect(readStatus.status, ProductResultV3.statusSuccess);
expect(readStatus.errors, isEmpty);
expect(readStatus.result, isNotNull);
expect(readStatus.result!.id, ProductResultV3.resultProductFound);
expect(readStatus.barcode, barcode);
expect(readStatus.product, isNotNull);
expect(readStatus.product!.packagingsComplete, value);
}
});
},
timeout: Timeout(
// some tests can be slow here
Expand Down

0 comments on commit ddd61a3

Please sign in to comment.