Bug 24228: Add parameters to Koha::Object(s)->to_api to automatically embed objects
authorAgustin Moyano <agustinmoyano@theke.io>
Thu, 19 Dec 2019 18:50:49 +0000 (15:50 -0300)
committerMartin Renvoize <martin.renvoize@ptfs-europe.com>
Wed, 8 Jan 2020 15:07:25 +0000 (15:07 +0000)
This patch makes Koha::Object(s)->to_api have an 'embeds' parameter that
using dot notation (e.g. resource.related_resource.another_one) allows
embedding objects recursively.

To test:
1. Apply this patch
2. prove t/db_dependent/Koha/Object.t

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Agustin Moyano <agustinmoyano@theke.io>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Koha/Object.pm
Koha/Objects.pm
t/db_dependent/Koha/Object.t

index e258606..b3ecb7f 100644 (file)
@@ -381,25 +381,57 @@ Returns a representation of the object, suitable for API output.
 =cut
 
 sub to_api {
-    my ( $self ) = @_;
+    my ( $self, $embeds ) = @_;
     my $json_object = $self->TO_JSON;
 
     my $to_api_mapping = $self->to_api_mapping;
 
     # Rename attributes if there's a mapping
-    foreach my $column ( keys %{$to_api_mapping} ) {
-        my $mapped_column = $to_api_mapping->{$column};
-        if ( exists $json_object->{$column}
-            && defined $mapped_column )
-        {
-            # key != undef
-            $json_object->{$mapped_column} = delete $json_object->{$column};
+    if ( $self->can('to_api_mapping') ) {
+        foreach my $column ( keys %{ $self->to_api_mapping } ) {
+            my $mapped_column = $self->to_api_mapping->{$column};
+            if ( exists $json_object->{$column}
+                && defined $mapped_column )
+            {
+                # key != undef
+                $json_object->{$mapped_column} = delete $json_object->{$column};
+            }
+            elsif ( exists $json_object->{$column}
+                && !defined $mapped_column )
+            {
+                # key == undef
+                delete $json_object->{$column};
+            }
         }
-        elsif ( exists $json_object->{$column}
-            && !defined $mapped_column )
-        {
-            # key == undef
-            delete $json_object->{$column};
+    }
+
+    if ($embeds) {
+        foreach my $embed (@$embeds) {
+            my ( $curr, $next ) = split /\s*\.\s*/, $embed, 2;
+            my @nxembeds;
+
+            @nxembeds = ($next) if $next;
+
+            my $children = $self->$curr;
+            if ( ref $children eq 'ARRAY' ) {
+                my @list;
+                my $pos = 0;
+                foreach my $child (@$children) {
+                    my $res = $child->to_api( \@nxembeds );
+                    $res = { $json_object->{$curr}->[$pos], $res }
+                      if defined $json_object->{$curr}
+                      && defined $json_object->{$curr}->[$pos];
+                    push @list, $res;
+                    $pos++;
+                }
+                $json_object->{$curr} = \@list;
+            }
+            else {
+                my $res = $children->to_api( \@nxembeds );
+                $res = { $json_object->{$curr}, $res }
+                  if defined $json_object->{$curr};
+                $json_object->{$curr} = $res;
+            }
         }
     }
 
index 165511a..0222771 100644 (file)
@@ -315,9 +315,9 @@ Returns a representation of the objects, suitable for API output .
 =cut
 
 sub to_api {
-    my ($self) = @_;
+    my ($self, $embeds) = @_;
 
-    return [ map { $_->to_api } $self->as_list ];
+    return [ map { $_->to_api($embeds) } $self->as_list ];
 }
 
 =head3 attributes_from_api
index b3c57bd..e8d1f36 100755 (executable)
@@ -215,7 +215,7 @@ subtest 'TO_JSON tests' => sub {
 
 subtest "to_api() tests" => sub {
 
-    plan tests => 11;
+    plan tests => 18;
 
     $schema->storage->txn_begin;
 
@@ -262,6 +262,44 @@ subtest "to_api() tests" => sub {
     my $illrequest = $builder->build_object({ class => 'Koha::Illrequests' });
     is_deeply( $illrequest->to_api, $illrequest->TO_JSON, 'If no overloaded to_api_mapping method, return TO_JSON' );
 
+    my $item_class = Test::MockModule->new('Koha::Item');
+    $item_class->mock( 'to_api_mapping',
+        sub {
+            return {
+                itemnumber       => 'item_id'
+            };
+        }
+    );
+
+    my $hold_class = Test::MockModule->new('Koha::Hold');
+    $hold_class->mock( 'to_api_mapping',
+        sub {
+            return {
+                reserve_id       => 'hold_id'
+            };
+        }
+    );
+
+    my $biblio = $builder->build_sample_biblio();
+    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
+    my $hold = $builder->build_object({ class => 'Koha::Holds', value => { itemnumber => $item->itemnumber } });
+
+    my @embeds = ('items');
+
+    my $biblio_api = $biblio->to_api(\@embeds);
+
+    ok(exists $biblio_api->{items}, 'Items where embedded in biblio results');
+    is($biblio_api->{items}->[0]->{item_id}, $item->itemnumber, 'Item matches');
+    ok(!exists $biblio_api->{items}->[0]->{holds}, 'No holds info should be embedded yet');
+
+    @embeds = ('items.holds');
+    $biblio_api = $biblio->to_api(\@embeds);
+
+    ok(exists $biblio_api->{items}, 'Items where embedded in biblio results');
+    is($biblio_api->{items}->[0]->{item_id}, $item->itemnumber, 'Item still matches');
+    ok(exists $biblio_api->{items}->[0]->{holds}, 'Holds info should be embedded');
+    is($biblio_api->{items}->[0]->{holds}->[0]->{hold_id}, $hold->reserve_id, 'Hold matches');
+
     $schema->storage->txn_rollback;
 };