Skip to content

Commit

Permalink
feat: Use 'GS1 Barcode Syntax Engine' in READ API v3 (#9050)
Browse files Browse the repository at this point in the history
  • Loading branch information
hangy authored Sep 22, 2023
1 parent 77d296f commit 85cee85
Show file tree
Hide file tree
Showing 28 changed files with 5,390 additions and 133 deletions.
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

0 comments on commit 85cee85

Please sign in to comment.