File Coverage

lib/Net/API/CPAN/Scroll.pm
Criterion Covered Total %
statement 22 72 30.5
branch 0 22 0.0
condition 0 17 0.0
subroutine 8 17 47.0
pod 8 8 100.0
total 38 136 27.9


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Meta CPAN API - ~/lib/Net/API/CPAN/Scroll.pm
3             ## Version v0.1.0
4             ## Copyright(c) 2023 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2023/08/03
7             ## Modified 2023/08/03
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Net::API::CPAN::Scroll;
15             BEGIN
16             {
17 2     2   104058 use strict;
  2         16  
  2         54  
18 2     2   11 use warnings;
  2         5  
  2         51  
19 2     2   517 use parent qw( Net::API::CPAN::List );
  2         309  
  2         8  
20 2     2   136 use vars qw( $VERSION );
  2         12  
  2         83  
21 2     2   11 use Want;
  2         2  
  2         117  
22 2     2   41 our $VERSION = 'v0.1.0';
23             };
24              
25 2     2   9 use strict;
  2         3  
  2         32  
26 2     2   9 use warnings;
  2         3  
  2         977  
27              
28             sub init
29             {
30 0     0 1   my $self = shift( @_ );
31 0 0         $self->{id} = undef unless( CORE::exists( $self->{id} ) );
32 0 0         $self->{size} = undef unless( CORE::exists( $self->{size} ) );
33 0 0         $self->{ttl} = undef unless( CORE::exists( $self->{ttl} ) );
34 0           $self->{_init_strict_use_sub} = 1;
35 0 0         $self->SUPER::init( @_ ) || return( $self->pass_error );
36 0           return( $self );
37             }
38              
39             sub close
40             {
41 0     0 1   my $self = shift( @_ );
42 0           my $opts = $self->_get_args_as_hash( @_ );
43 0   0       $opts->{all} //= 0;
44 0           my $uri = $self->uri;
45 0 0         if( $opts->{all} )
46             {
47             # e.g.: /v1/author/_search/scroll -> /v1/author/_search/scroll/_all
48 0           $uri->path( $uri->path . '/_all' );
49             }
50 0   0       my $id = $self->id ||
51             return( $self->error( "No scroll ID was set to close." ) );
52 0           my $hash = { scroll_id => [$id] };
53 0           my $payload;
54 0           local $@;
55             # try-catch
56             eval
57 0           {
58 0           $payload = $self->new_json->encode( $hash );
59             };
60 0 0         if( $@ )
61             {
62 0           return( $self->error( "An error occured while encoding JSON payload to remove scroller: $@" ) );
63             }
64 0   0       my $ua = $self->ua ||
65             return( $self->error( "No HTTP::Promise object current set." ) );
66 0   0       my $resp = $ua->delete( $uri,
67             Content => $payload,
68             Accept => 'application/json',
69             ) || return( $self->pass_error( $ua->error ) );
70 0           return( $resp );
71             }
72              
73             # e.g.: cXVlcnlUaGVuRmV0Y2g7Mzs0MDE0MzQ1MTQ6N2NvRzNSdklTYkdiRmNPNi04VXFjQTs2NzEwNTc1NTE6OWtIOUE2b2xUaHk3cU5iWkl6ajZrUTsxMDcyNDY5OTMxOk1lZVhCR1J4VG1tT0QxWjRFd2J0Z2c7MDs=
74             # which is a base64 that translates to:
75             # queryThenFetch;3;401434514:7coG3RvISbGbFcO6-8UqcA;671057551:9kH9A6olThy7qNbZIzj6kQ;1072469931:MeeXBGRxTmmOD1Z4Ewbtgg;0;
76 0     0 1   sub id { return( shift->_set_get_scalar( 'id', @_ ) ); }
77              
78             sub postprocess
79             {
80 0     0 1   my $self = shift( @_ );
81 0           my $ref = shift( @_ );
82 0   0       my $id = $ref->{_scroll_id} || return( $self->error( "No scroll ID was returned by the MetaCPAN API" ) );
83 0           $self->id( $id );
84 0           return( $self );
85             }
86              
87 0     0 1   sub time { return( shift->_set_get_scalar_as_object( 'ttl', @_ ) ); }
88              
89 0     0 1   sub ttl { return( shift->_set_get_scalar_as_object( 'ttl', @_ ) ); }
90              
91 0     0 1   sub type { return( shift->_set_get_scalar_as_object( 'type', @_ ) ); }
92              
93             sub uri
94             {
95 0     0 1   my $self = shift( @_ );
96 0   0       my $uri = $self->SUPER::uri || return( $self->pass_error );
97             # e.g.: /v1/author/_search -> /v1/author/_search/scroll
98 0           $uri->path( $uri->path . '/scroll' );
99 0   0       my $filter = $self->filter ||
100             return( $self->error( "No search filter is set!" ) );
101 0   0       my $size = $self->size // $filter->size;
102 0           my $q = {};
103 0 0         if( defined( my $ttl = $self->ttl ) )
104             {
105 0           $q->{scroll} = $ttl;
106             }
107 0 0         if( defined( $size ) )
108             {
109 0           $q->{size} = $size;
110             }
111 0 0         if( defined( my $id = $self->id ) )
112             {
113 0           $q->{scroll_id} = $id;
114             }
115 0 0         $uri->query_form( $q ) if( scalar( keys( %$q ) ) );
116 0           return( $uri );
117             }
118              
119             sub DESTROY
120             {
121 0     0     my $self = shift( @_ );
122 0 0         if( defined( my $id = $self->id ) )
123             {
124 0           $self->close;
125             }
126             }
127              
128             1;
129             # NOTE: POD
130             __END__
131              
132             =encoding utf-8
133              
134             =head1 NAME
135              
136             Net::API::CPAN::Scroll - Meta CPAN API Search Scroller
137              
138             =head1 SYNOPSIS
139              
140             use Net::API::CPAN::Scroll;
141             my $this = Net::API::CPAN::Scroll->new(
142             time => '1m',
143             size => 1000,
144             ) || die( Net::API::CPAN::Scroll->error, "\n" );
145              
146             =head1 VERSION
147              
148             v0.1.0
149              
150             =head1 DESCRIPTION
151              
152             This class is used to access a list of data like L<Net::API::CPAN::List> from which it inherits, but uses L<Elastic Search scroller|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html>. See the L<perl module|Search::Elasticsearch::Client::8_0::Scroll>
153              
154             Note that with the scroll search, you can only scroll forward and not backward, which means you can only use L<next|Net::API::CPAN::List/next>, but not L<prev|Net::API::CPAN::List/prev>
155              
156             =head1 CONSTRUCTOR
157              
158             =head2 new
159              
160             Provided with an hash or an hash reference of parameters and this will instantiate a new list object.
161              
162             The valid parmeters that can be used are as below and can also be accessed with their corresponding method:
163              
164             =over 4
165              
166             =item * C<api>
167              
168             An L<Net::API::CPAN> object.
169              
170             =item * C<items>
171              
172             An array reference of data.
173              
174             =back
175              
176             =head1 METHODS
177              
178             =head2 close
179              
180             my $resp = $scroll->close; # returns an HTTP::Promise::Response object
181              
182             If a scroll ID is set, this will issue a C<DELETE> C<HTTP> query to clear the scroll, and return the resulting L<HTTP::Promise::Response>, or, upon error, this will set an L<error object|Net::API::CPAN::Exception> and return C<undef> in scalar context, or an empty list in list context.
183              
184             The C<HTTP> payload would look like something this:
185              
186             {
187             "scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"]
188             }
189              
190             Alternatively, if you pass the option C<all> with a true value, this will issue an C<HTTP> query to clear all scroll.
191              
192             See also L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html#_clear_scroll_api>
193              
194             =head2 id
195              
196             Sets or gets the scroll ID as returned by the MetaCPAN API in the C<_scroll_id> property.
197              
198             It returns a regular string representing the scroll ID, or C<undef> if none were set.
199              
200             =head2 postprocess
201              
202             This method is called by L</load> with the hash reference of data received from the MetaCPAN API, for possible post processing.
203              
204             It returns the current object for chaining, or upon error, sets an L<error|Net::API::CPAN::Exception> and returns C<undef> in scalar context or an empty list in list context.
205              
206             =head2 size
207              
208             Sets or gets the size of the data to be returned by the Elastic Search.
209              
210             Returns a L<number object|Module::Generic::Number>, or C<undef> if an L<error|Net::API::CPAN::Exception> occurred.
211              
212             =head2 time
213              
214             Same as L</ttl>
215              
216             =head2 ttl
217              
218             $scroll->ttl( '1m' );
219             my $time = $scroll->ttl;
220              
221             Sets or gets the value for L<how long the data should be kept alive by Elastic Search|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html#scroll-search-context>.
222              
223             Possible unit to use next to the integer are:
224              
225             =over 4
226              
227             =item * C<y> Year
228              
229             =item * C<M> Month
230              
231             =item * C<w> Week
232              
233             =item * C<d> Day
234              
235             =item * C<h> Hour
236              
237             =item * C<m> Minute
238              
239             =item * C<s> Second
240              
241             =item * C<ms> Milli-second
242              
243             =back
244              
245             See L<Elastic Search documentation on valid units|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/common-options.html#time-units>
246              
247             Returns a L<scalar object|Module::Generic::Scalar> upon success, or sets an error L<Net::API::CPAN::Exception> and returns C<undef> in scalar context, or an empty list in list context.
248              
249             =head2 uri
250              
251             Returns the L<URI> to use for the scroll search, which will contain some query string even if the query is using C<HTTP> C<POST> method. For example:
252              
253             POST /v1/author/_search?scroll=1m
254             {
255             "size": 100,
256             "query": {
257             "match": {
258             "message": "foo"
259             }
260             }
261             }
262              
263             =head1 AUTHOR
264              
265             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
266              
267             =head1 SEE ALSO
268              
269             L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html>
270              
271             L<StackOverflow|https://stackoverflow.com/questions/46604207/elasticsearch-scroll>
272              
273             =head1 COPYRIGHT & LICENSE
274              
275             Copyright(c) 2023 DEGUEST Pte. Ltd.
276              
277             All rights reserved
278              
279             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
280              
281             =cut