Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use 'GS1 Barcode Syntax Engine' in READ API v3 #9050

Merged
merged 23 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a34d6ee
chg: Support GS1 barcodes for code normalization
hangy Aug 25, 2023
705ed25
fix: Minor mistakes
hangy Aug 25, 2023
d5a6c40
fix: perl tidy and critic
hangy Aug 26, 2023
aec591d
test: Add additional test for normalization
hangy Sep 21, 2023
442dd4e
chg: Catch GS1::SyntaxEngine exceptions
hangy Aug 26, 2023
0085a93
test: Some more cases
hangy Sep 21, 2023
07b2fba
fix: Don't try to normalize undef code
hangy Sep 21, 2023
777895f
test: Add additional tests for `normalize_code`
hangy Aug 29, 2023
c36669e
docs: Update POD for `normalize_code()`
hangy Sep 11, 2023
53e2cd0
fix: URI and URL should both be valid in code
hangy Sep 16, 2023
f93ab32
wip: Return AI data string from normalize_requested_code
hangy Sep 11, 2023
88533fd
fix: Export normalize_code_with_gs1_ai
hangy Sep 16, 2023
745e0dd
test: Add some integration tests for READ API with simple GS1 cases
hangy Sep 16, 2023
afc862c
chg: Try to detect GTIN from GS1 AI string, data URI, etc.
hangy Sep 16, 2023
9b9bfa9
test: Add get-existing-product-gs1-data-uri test case
hangy Sep 16, 2023
9691b71
test: Add test cases for normalize_code_with_gs1_ai
hangy Sep 16, 2023
5590d41
fix: Reverse order of UTF8 decoding and removal of query parameters f…
hangy Sep 16, 2023
dae1b20
test: Add test cases for analyze_request with GS1 data uri
hangy Sep 16, 2023
974b606
fix: Adjust my expectectations, and reverse order of split by '/' , a…
hangy Sep 17, 2023
604d842
fix: Always use results from `normalize_code_with_gs1_ai`, just disca…
hangy Sep 17, 2023
34e0282
fix: Update expected test results
hangy Sep 18, 2023
da8c122
fix: Update expected test results after merge
hangy Sep 19, 2023
8696c02
Merge remote-tracking branch 'origin/main' into issue/8333/read-api
hangy Sep 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions cgi/search.pl
Original file line number Diff line number Diff line change
Expand Up @@ -138,31 +138,31 @@
}

# check if the search term looks like a barcode

if ( (not defined single_param('json'))
and (not defined single_param('jsonp'))
and (not defined single_param('jqm'))
and (not defined single_param('jqm_loadmore'))
and (not defined single_param('xml'))
and (not defined single_param('rss'))
and ($search_terms =~ /^(\d{4,24})$/))
and ($search_terms =~ /^(\d{4,24}|(?:[\^(\N{U+001D}\N{U+241D}]|https?:\/\/).+)$/))
{

my $code = normalize_code($search_terms);
if ((defined $code) and (length($code) > 0)) {
my $product_id = product_id_for_owner($Owner_id, $code);

my $product_id = product_id_for_owner($Owner_id, $code);

my $product_ref = product_exists($product_id); # returns 0 if not
my $product_ref = product_exists($product_id); # returns 0 if not

if ($product_ref) {
$log->info("product code exists, redirecting to product page", {code => $code});
my $location = product_url($product_ref);
if ($product_ref) {
$log->info("product code exists, redirecting to product page", {code => $code});
my $location = product_url($product_ref);

my $r = shift;
$r->headers_out->set(Location => $location);
$r->status(301);
return 301;
my $r = shift;
$r->headers_out->set(Location => $location);
$r->status(301);
return 301;

}
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/ProductOpener/API.pm
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,13 @@ Reference to the response object.

=head3 Return value

Normalized code.
Normalized code and, if available, GS1 AI data string.

=cut

sub normalize_requested_code ($requested_code, $response_ref) {

my $code = normalize_code($requested_code);
my ($code, $ai_data_str) = &normalize_code_with_gs1_ai($requested_code);
$response_ref->{code} = $code;

# Add a warning if the normalized code is different from the requested code
Expand All @@ -470,7 +470,7 @@ sub normalize_requested_code ($requested_code, $response_ref) {
);
}

return $code;
return ($code, $ai_data_str);
}

=head2 get_images_to_update($product_ref, $target_lc)
Expand Down
2 changes: 1 addition & 1 deletion lib/ProductOpener/APIProductRead.pm
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ sub read_product_api ($request_ref) {
|| "";
}

my $code = normalize_requested_code($request_ref->{code}, $response_ref);
my ($code, $ai_data_string) = &normalize_requested_code($request_ref->{code}, $response_ref);

my $product_ref;
my $product_id;
Expand Down
2 changes: 1 addition & 1 deletion lib/ProductOpener/APIProductWrite.pm
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ sub write_product_api ($request_ref) {

if ($code ne 'test') {
# Load the product
$code = normalize_requested_code($request_ref->{code}, $response_ref);
($code, my $ai_data_string) = &normalize_requested_code($request_ref->{code}, $response_ref);

# Check if the code is valid
if ($code !~ /^\d{4,24}$/) {
Expand Down
39 changes: 34 additions & 5 deletions lib/ProductOpener/Products.pm
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ BEGIN {
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS);
@EXPORT_OK = qw(
&normalize_code
&normalize_code_with_gs1_ai
&assign_new_code
&split_code
&product_id_for_owner
Expand Down Expand Up @@ -280,9 +281,36 @@ Normalized version of the code
sub normalize_code ($code) {

if (defined $code) {
my $gs1_code = _try_normalize_code_gs1($code);
if ($gs1_code) {
($code, my $gs1_ai_data_str) = &normalize_code_with_gs1_ai($code);
}
return $code;
}

=head2 normalize_code_with_gs1_ai()

C<normalize_code_with_gs1_ai()> this function normalizes the product code by:
- running the given code through normalization method provided by GS1 to format a GS1 data string, or data URI to a GTIN,
- keeping only digits and removing spaces/dashes etc.,
- normalizing the length by adding leading zeroes or removing the leading zero (in case of 14 digit codes)

=head3 Arguments

Product Code in the Raw form: $code

=head3 Return Values

Normalized version of the code, and GS1 AI data string of the code, if a valid GS1 string was given as the argument

=cut

sub normalize_code_with_gs1_ai ($code) {

my $ai_data_str;
if (defined $code) {
my ($gs1_code, $gs1_ai_data_str) = &_try_normalize_code_gs1($code);
if ($gs1_code and $gs1_ai_data_str) {
$code = $gs1_code;
$ai_data_str = $gs1_ai_data_str;
}

# Keep only digits, remove spaces, dashes and everything else
Expand All @@ -308,7 +336,7 @@ sub normalize_code ($code) {
$code = $';
}
}
return $code;
return ($code, $ai_data_str);
}

sub _try_normalize_code_gs1 ($code) {
Expand Down Expand Up @@ -346,11 +374,12 @@ sub _try_normalize_code_gs1 ($code) {
};
if ($@) {
$log->warn("GS1Parser error", {error => $@}) if $log->is_warn();
$code = undef;
$ai_data_str = undef;
}

if ((defined $ai_data_str) and ($ai_data_str =~ /^\(01\)(\d{1,14})/)) {
return $1;
if ((defined $code) and (defined $ai_data_str) and ($ai_data_str =~ /^\(01\)(\d{1,14})/)) {
return ($1, $ai_data_str);
}
else {
return;
Expand Down
26 changes: 13 additions & 13 deletions lib/ProductOpener/Routing.pm
Original file line number Diff line number Diff line change
Expand Up @@ -148,33 +148,33 @@ sub analyze_request ($request_ref) {
{query_string => $request_ref->{query_string}})
if $log->is_debug();

# Decode the escaped characters in the query string
$request_ref->{query_string} = decode("utf8", URI::Escape::XS::decodeURIComponent($request_ref->{query_string}));

$log->debug("analyzing query_string, step 3 - components UTF8 decoded",
{query_string => $request_ref->{query_string}})
if $log->is_debug();

$request_ref->{page} = 1;

# some sites like FB can add query parameters, remove all of them
# make sure that all query parameters of interest have already been consumed above

$request_ref->{query_string} =~ s/(\&|\?).*//;

$log->debug("analyzing query_string, step 4 - removed all query parameters",
$log->debug("analyzing query_string, step 3 - removed all query parameters",
{query_string => $request_ref->{query_string}})
if $log->is_debug();

# Split query string by "/" to know where it points
my @components = ();
foreach my $component (split(/\//, $request_ref->{query_string})) {
# Decode the escaped characters in the query string
push(@components, decode("utf8", URI::Escape::XS::decodeURIComponent($component)));
}

$log->debug("analyzing query_string, step 4 - components split and UTF8 decoded", {components => \@components})
if $log->is_debug();

$request_ref->{page} = 1;

# if the query request json or xml, either through the json=1 parameter or a .json extension
# set the $request_ref->{api} field
if ((defined single_param('json')) or (defined single_param('jsonp')) or (defined single_param('xml'))) {
$request_ref->{api} = 'v0';
}

# Split query string by "/" to know where it points
my @components = split(/\//, $request_ref->{query_string});

# Root, ex: https://world.openfoodfacts.org/
if ($#components < 0) {
$request_ref->{text} = 'index';
Expand Down
62 changes: 46 additions & 16 deletions tests/integration/api_v3_product_read.t
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ my @products = (
{
%{dclone(\%default_product_form)},
(
code => '200000000034',
code => '4260392550101',
product_name => "Some product",
generic_name => "Tester",
ingredients_text => "apple, milk, eggs, palm oil",
Expand Down Expand Up @@ -56,90 +56,120 @@ my $tests_ref = [
{
test_case => 'get-existing-product',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
expected_status_code => 200,
},
{
test_case => 'get-existing-product-gs1-caret',
method => 'GET',
path => '/api/v3/product/%5E0104260392550101',
expected_status_code => 200,
},
{
test_case => 'get-existing-product-gs1-fnc1',
method => 'GET',
path => '/api/v3/product/%1D0104260392550101',
expected_status_code => 200,
},
{
test_case => 'get-existing-product-gs1-gs',
method => 'GET',
path => '/api/v3/product/%E2%90%9D0104260392550101',
expected_status_code => 200,
},
{
test_case => 'get-existing-product-gs1-ai-data-str',
method => 'GET',
path => '/api/v3/product/(01)04260392550101',
expected_status_code => 200,
},
{
test_case => 'get-existing-product-gs1-data-uri',
method => 'GET',
path => '/api/v3/product/https%3A%2F%2Fid.gs1.org%2F01%2F04260392550101%2F10%2FABC%2F21%2F123456%3F17%3D211200',
expected_status_code => 200,
},
{
test_case => 'get-specific-fields',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=product_name,categories_tags,categories_tags_en',
expected_status_code => 200,
},
{
test_case => 'get-images-to-update',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=images_to_update_en',
expected_status_code => 200,
},
{
test_case => 'get-attribute-groups',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=attribute_groups',
expected_status_code => 200,
},
{
test_case => 'get-attribute-groups-fr',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=attribute_groups&lc=fr',
expected_status_code => 200,
},
{
test_case => 'get-knowledge-panels',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=knowledge_panels',
expected_status_code => 200,
},
{
test_case => 'get-knowledge-panels-fr',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=knowledge_panels&lc=fr',
expected_status_code => 200,
},
{
test_case => 'get-packagings',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=packagings',
expected_status_code => 200,
},
{
test_case => 'get-packagings-fr',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=packagings&tags_lc=fr',
expected_status_code => 200,
},
{
test_case => 'get-fields-raw',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=raw',
expected_status_code => 200,
},
{
test_case => 'get-fields-all',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=all',
expected_status_code => 200,
},
{
test_case => 'get-fields-all-knowledge-panels',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=all,knowledge_panels',
expected_status_code => 200,
},
{
test_case => 'get-fields-attribute-groups-all-knowledge-panels',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=attribute_groups,all,knowledge_panels',
expected_status_code => 200,
},
Expand All @@ -148,14 +178,14 @@ my $tests_ref = [
{
test_case => 'get-auth-good-password',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=code,product_name&user_id=tests&password=testtest',
expected_status_code => 200,
},
{
test_case => 'get-auth-bad-user-password',
method => 'GET',
path => '/api/v3/product/200000000034',
path => '/api/v3/product/4260392550101',
query_string => '?fields=code,product_name&user_id=tests&password=bad_password',
expected_status_code => 200,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"code" : "200000000034",
"code" : "4260392550101",
"errors" : [],
"product" : {
"attribute_groups" : [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"code" : "200000000034",
"code" : "4260392550101",
"errors" : [],
"product" : {
"attribute_groups" : [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"code" : "200000000034",
"code" : "4260392550101",
"errors" : [],
"product" : {
"code" : "200000000034",
"code" : "4260392550101",
"product_name" : "Some product"
},
"result" : {
Expand Down
Loading