From 3765277cb2f09a15e503c81c6e9e73848d00f0a8 Mon Sep 17 00:00:00 2001 From: Nathan Levin-Greenhaw Date: Thu, 30 Jun 2016 08:28:33 -0700 Subject: [PATCH 1/4] fix for issue #28 --- lib/Rethinkdb/IO.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Rethinkdb/IO.pm b/lib/Rethinkdb/IO.pm index f5912e6..25b1873 100644 --- a/lib/Rethinkdb/IO.pm +++ b/lib/Rethinkdb/IO.pm @@ -346,6 +346,9 @@ sub _send { $self->_handle->recv( $length, 4 ); $length = unpack 'L<', $length; + # if we couldn't unpack a length, say it is zero + $length ||= 0; + my $_data; do { $self->_handle->recv( $_data, 4096 ); From 041123159817207945fecb43e932452d1ee51666 Mon Sep 17 00:00:00 2001 From: Nathan Levin-Greenhaw Date: Fri, 1 Jul 2016 09:22:32 -0700 Subject: [PATCH 2/4] adding new RethinkDB v2.3.4 functionality & tests --- README.md | 3 +- external/ql2.proto | 44 +++++++---- lib/Rethinkdb.pm | 51 ++++++++++++- lib/Rethinkdb/IO.pm | 49 ++++++++++-- lib/Rethinkdb/Protocol.pm | 7 ++ lib/Rethinkdb/Query.pm | 78 +++++++++++++++++++- lib/Rethinkdb/Query/Database.pm | 21 ++++++ lib/Rethinkdb/Query/Table.pm | 23 +++++- t/admin.t | 127 +++++++++++++++++++++++++++++++- t/aggregation.t | 53 +++++++++++-- t/connect.t | 53 ++++++++++--- t/document.t | 27 +++++-- 12 files changed, 477 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index ab72a82..0d17039 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ See http://njlg.info/perl-rethinkdb/ ## Notes -* This version is compatible with RethinkDB 2.1.3 +* This version is compatible with RethinkDB 2.3.4 + * No authentication support yet * This is still in beta stage * For examples see the tests in `t/*.t` or see the documentation (link above) diff --git a/external/ql2.proto b/external/ql2.proto index 2898045..e40c5be 100644 --- a/external/ql2.proto +++ b/external/ql2.proto @@ -48,6 +48,7 @@ message VersionDummy { // We need to wrap it like this for some V0_2 = 0x723081e1; // Authorization key during handshake V0_3 = 0x5f75e83e; // Authorization key and protocol during handshake V0_4 = 0x400c2d20; // Queries execute in parallel + V1_0 = 0x34c2bdc3; // Users and permissions } // The protocol to use after the handshake, specified in V0_3 @@ -64,14 +65,15 @@ message VersionDummy { // We need to wrap it like this for some // * A [STOP] query with the same token as a [START] query that you want to stop. // * A [NOREPLY_WAIT] query with a unique per-connection token. The server answers // with a [WAIT_COMPLETE] [Response]. +// * A [SERVER_INFO] query. The server answers with a [SERVER_INFO] [Response]. message Query { enum QueryType { - START = 1; // Start a new query. - CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL] - // (see [Response]). - STOP = 3; // Stop a query partway through executing. - NOREPLY_WAIT = 4; - // Wait for noreply operations to finish. + START = 1; // Start a new query. + CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL] + // (see [Response]). + STOP = 3; // Stop a query partway through executing. + NOREPLY_WAIT = 4; // Wait for noreply operations to finish. + SERVER_INFO = 5; // Get server information. } optional QueryType type = 1; // A [Term] is how we represent the operations we want a query to perform. @@ -97,8 +99,8 @@ message Query { // A backtrace frame (see `backtrace` in Response below) message Frame { enum FrameType { - POS = 1; // Error occured in a positional argument. - OPT = 2; // Error occured in an optional argument. + POS = 1; // Error occurred in a positional argument. + OPT = 2; // Error occurred in an optional argument. } optional FrameType type = 1; optional int64 pos = 2; // The index of the positional argument. @@ -120,6 +122,9 @@ message Response { // more of the sequence. Keep sending [CONTINUE] // queries until you get back [SUCCESS_SEQUENCE]. WAIT_COMPLETE = 4; // A [NOREPLY_WAIT] query completed. + SERVER_INFO = 5; // The data for a [SERVER_INFO] request. This is + // the same as `SUCCESS_ATOM` except that there will + // never be profiling data. // These response types indicate failure. CLIENT_ERROR = 16; // Means the client is buggy. An example is if the @@ -145,6 +150,7 @@ message Response { OP_FAILED = 4100000; OP_INDETERMINATE = 4200000; USER = 5000000; + PERMISSION_ERROR = 6000000; } optional ErrorType error_type = 7; @@ -174,14 +180,15 @@ message Response { optional int64 token = 2; // Indicates what [Query] this response corresponds to. - // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM], or many RQL - // data if [type] is [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. It contains 1 + // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM] or + // [SERVER_INFO]. [response] contains many RQL data if [type] is + // [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. [response] contains 1 // error message (of type [R_STR]) in all other cases. repeated Datum response = 3; // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a // backtrace will be provided. The backtrace says where in the query the - // error occured. Ideally this information will be presented to the user as + // error occurred. Ideally this information will be presented to the user as // a pretty-printed version of their query with the erroneous section // underlined. A backtrace is a series of 0 or more [Frame]s, each of which // specifies either the index of a positional argument or the name of an @@ -227,8 +234,6 @@ message Datum { optional Datum val = 2; } repeated AssocPair r_object = 6; - - extensions 10000 to 20000; } // A [Term] is either a piece of data (see **Datum** above), or an operator and @@ -383,6 +388,8 @@ message Term { // | Sequence, STRING -> Sequence // Return an array containing the keys of the object. KEYS = 94; // OBJECT -> ARRAY + // Return an array containing the values of the object. + VALUES = 186; // OBJECT -> ARRAY // Creates an object OBJECT = 143; // STRING, DATUM, ... -> OBJECT // Check whether an object contains all the specified fields, @@ -413,6 +420,8 @@ message Term { // The arity of the function should be // Sequence..., Function(sizeof...(Sequence)) -> Sequence + FOLD = 187; // Sequence, Datum, Function(2), {Function(3), Function(1) + // Filter a sequence with either a function or a shortcut // object (see API docs for details). The body of FILTER is // wrapped in an implicit `.default(false)`, and you can @@ -545,6 +554,11 @@ message Term { // written to disk. SYNC = 138; // Table -> OBJECT + // Set global, database, or table-specific permissions + GRANT = 188; // -> OBJECT + // Database -> OBJECT + // Table -> OBJECT + // * Secondary indexes OPs // Creates a new secondary index with a particular name and definition. INDEX_CREATE = 75; // Table, STRING, Function(1), {multi:BOOL} -> OBJECT @@ -554,7 +568,7 @@ message Term { INDEX_LIST = 77; // Table -> ARRAY // Gets information about whether or not a set of indexes are ready to // be accessed. Returns a list of objects that look like this: - // {index:STRING, ready:BOOL[, blocks_processed:NUMBER, blocks_total:NUMBER]} + // {index:STRING, ready:BOOL[, progress:NUMBER]} INDEX_STATUS = 139; // Table, STRING... -> ARRAY // Blocks until a set of indexes are ready to be accessed. Returns the // same values INDEX_STATUS. @@ -779,8 +793,6 @@ message Term { repeated AssocPair optargs = 4; // Holds the optional arguments of the query. // (Note that the order of the optional arguments doesn't matter; think of a // Hash.) - - extensions 10000 to 20000; } //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Rethinkdb.pm b/lib/Rethinkdb.pm index 214bf9a..3bf24dd 100644 --- a/lib/Rethinkdb.pm +++ b/lib/Rethinkdb.pm @@ -63,6 +63,12 @@ sub connect { return $io->connect; } +sub server { + my $self = shift; + + return $self->io->server; +} + # DATABASES sub db_create { @@ -110,9 +116,9 @@ sub db { my $name = shift; my $db = Rethinkdb::Query::Database->new( - _rdb => $self, - name => $name, - args => $name, + _rdb => $self, + name => $name, + args => $name, ); weaken $db->{_rdb}; @@ -811,6 +817,20 @@ sub floor { return $q; } +sub grant { + my $self = shift; + my $user = shift; + my $perms = shift; + + my $q = Rethinkdb::Query->new( + _rdb => $self, + _type => $self->term->termType->grant, + args => [ $user, $perms ] + ); + + return $q; +} + sub true { return Rethinkdb::_True->new; } sub false { return Rethinkdb::_False->new; } @@ -912,6 +932,25 @@ connection will be set via C in the new instance. Create a new connection to a RethinkDB shard. Creating a connection tries to contact the RethinkDB shard immediately and will fail if the connection fails. +=head2 server + + r->server->run; + +Return information about the server being used by the default connection. + +The server command returns either two or three fields: + +=over + +=item C: the UUID of the server the client is connected to. + +=item C: a boolean indicating whether the server is a L. + +=item C: the server name. If proxy is Ctrue>, this field will not be +returned. + +=back + =head2 db_create r->db_create('test')->run; @@ -1406,6 +1445,12 @@ or equal to the given value (the value's ceiling). Rounds the given value down, returning the largest integer value less than or equal to the given value (the value's floor). +=head2 grant + + r->grant('username', {read => r->true, write => r->false })->run; + +Grant or deny access permissions for a user account globally. + =head2 true r->true->run; diff --git a/lib/Rethinkdb/IO.pm b/lib/Rethinkdb/IO.pm index 25b1873..c44287f 100644 --- a/lib/Rethinkdb/IO.pm +++ b/lib/Rethinkdb/IO.pm @@ -110,6 +110,17 @@ sub noreply_wait { ); } +sub server { + my $self = shift; + + return $self->_send( + { + type => $self->_protocol->query->queryType->server_info, + token => Rethinkdb::Util::_token(), + } + ); +} + sub _start { my $self = shift; my ( $query, $args, $callback ) = @_; @@ -125,7 +136,7 @@ sub _start { } # add our database - if(!$args->{db}) { + if ( !$args->{db} ) { $args->{db} = $self->default_db; } @@ -159,10 +170,12 @@ sub _simple_encode_hash { } if ( $json->{db} ) { - $json->{db} = Rethinkdb::IO->_encode_recurse(Rethinkdb::Query::Database->new( - name => $json->{db}, - args => $json->{db}, - )->_build); + $json->{db} = Rethinkdb::IO->_encode_recurse( + Rethinkdb::Query::Database->new( + name => $json->{db}, + args => $json->{db}, + )->_build + ); } return $json; @@ -359,8 +372,8 @@ sub _send { my $res_data = $self->_decode($data); $res_data->{token} = $token; - # handle partial and feed responses - if ( $res_data->{t} == 3 or $res_data->{t} == 5 ) { + # handle partial response + if ( $res_data->{t} == 3 ) { if ( $self->_callbacks->{$token} ) { my $res = Rethinkdb::Response->_init( $res_data, $args ); @@ -386,7 +399,7 @@ sub _send { say {*STDERR} Dumper $res_data; } - # fetch the rest of the data if stream/partial/feed + # fetch the rest of the data if partial my $more = $self->_send( { type => $self->_protocol->query->queryType->continue, @@ -547,6 +560,26 @@ use this connection. The C method will tell the database to wait until all "no reply" have executed before responding. +=head2 server + + my $conn = r->connect; + $conn->server; + +Return information about the server being used by this connection. + +The server command returns either two or three fields: + +=over + +=item C: the UUID of the server the client is connected to. + +=item C: a boolean indicating whether the server is a L. + +=item C: the server name. If proxy is Ctrue>, this field will not be +returned. + +=back + =head1 SEE ALSO L, L diff --git a/lib/Rethinkdb/Protocol.pm b/lib/Rethinkdb/Protocol.pm index 548c040..f6d8063 100644 --- a/lib/Rethinkdb/Protocol.pm +++ b/lib/Rethinkdb/Protocol.pm @@ -23,6 +23,7 @@ has 'v0_1' => 0x3f61ba36; has 'v0_2' => 0x723081e1; has 'v0_3' => 0x5f75e83e; has 'v0_4' => 0x400c2d20; +has 'v1_0' => 0x34c2bdc3; package Rethinkdb::Protocol::Protocol; use Rethinkdb::Base -base; @@ -39,6 +40,7 @@ has 'start' => 1; has 'continue' => 2; has 'stop' => 3; has 'noreply_wait' => 4; +has 'server_info' => 5; package Rethinkdb::Protocol::Frame; use Rethinkdb::Base -base; @@ -64,6 +66,7 @@ has 'success_atom' => 1; has 'success_sequence' => 2; has 'success_partial' => 3; has 'wait_complete' => 4; +has 'server_info' => 5; has 'client_error' => 16; has 'compile_error' => 17; has 'runtime_error' => 18; @@ -77,6 +80,7 @@ has 'non_existence' => 3100000; has 'op_failed' => 4100000; has 'op_indeterminate' => 4200000; has 'user' => 5000000; +has 'permission_error' => 6000000; package Rethinkdb::Protocol::ResponseNote; use Rethinkdb::Base -base; @@ -148,6 +152,7 @@ has 'offsets_of' => 87; has 'contains' => 93; has 'get_field' => 31; has 'keys' => 94; +has 'values' => 186; has 'object' => 143; has 'has_fields' => 32; has 'with_fields' => 96; @@ -158,6 +163,7 @@ has 'between_deprecated' => 36; has 'between' => 182; has 'reduce' => 37; has 'map' => 38; +has 'fold' => 187; has 'filter' => 39; has 'concat_map' => 40; has 'order_by' => 41; @@ -194,6 +200,7 @@ has 'wait' => 177; has 'reconfigure' => 176; has 'rebalance' => 179; has 'sync' => 138; +has 'grant' => 188; has 'index_create' => 75; has 'index_drop' => 76; has 'index_list' => 77; diff --git a/lib/Rethinkdb/Query.pm b/lib/Rethinkdb/Query.pm index 7064831..ebbc48e 100644 --- a/lib/Rethinkdb/Query.pm +++ b/lib/Rethinkdb/Query.pm @@ -45,6 +45,7 @@ sub _build { } } } + # else { # push @{ $q->{args} }, undef; # } @@ -412,16 +413,16 @@ sub sample { # AGGREGATION sub group { - my $self = shift; - my $args = [@_]; + my $self = shift; + my $args = [@_]; my $optargs = {}; if ( ref $args->[ $#{$args} ] eq 'HASH' ) { $optargs = pop @{$args}; } - if( ref $args->[0] && ref $args->[0] ne 'CODE' ) { - $args = Rethinkdb::Util->_wrap_func($args->[0]); + if ( ref $args->[0] && ref $args->[0] ne 'CODE' ) { + $args = Rethinkdb::Util->_wrap_func( $args->[0] ); } my $q = Rethinkdb::Query->new( @@ -458,6 +459,29 @@ sub reduce { return $q; } +sub fold { + my $self = shift; + my $acc = shift; + my $fn = shift; + my $emitter = shift; + + my $args = [$acc, $fn]; + my $optargs = {}; + + if($emitter) { + $optargs = { emit => $emitter }; + } + + my $q = Rethinkdb::Query->new( + _parent => $self, + _type => $self->_termType->fold, + args => $args, + optargs => $optargs + ); + + return $q; +} + sub count { my $self = shift; my $args = shift; @@ -793,6 +817,19 @@ sub keys { return $q; } +sub values { + my $self = shift; + my $args = [@_]; + + my $q = Rethinkdb::Query->new( + _parent => $self, + _type => $self->_termType->values, + args => $args, + ); + + return $q; +} + # STRING MANIPULATION sub match { @@ -1638,6 +1675,32 @@ if you want to e.g. order the groups by the value of their reduction. Produce a single value from a sequence through repeated application of a reduction function. +=head2 fold + + r->table('words')->order_by('id')->fold( + '', + sub ($$) { + my ( $acc, $word ) = @_; + return $acc->add( r->branch( $acc->eq(''), '', ', ' )->add($word) ); + } + )->run; + + r->table('tracker')->filter( { name => 'bob' } )->order_by('date') + ->bracket('weight')->fold( + [], + sub ($$) { + my ( $acc, $row ) = @_; + return $acc->append($row)->limit(5); + }, + sub ($$$) { + my ( $acc, $row, $new_acc ) = @_; + return r->branch( new_acc->size()->eq(5), [ new_acc->avg() ], [] ); + } + )->run; + +Apply a function to a sequence in order, maintaining state via an accumulator. +The fold command returns either a single value or a new sequence. + =head2 count r->table('marvel')->count->add(r->table('dc')->count->run @@ -1844,6 +1907,13 @@ Change a value in an array at a given index. Returns the modified array. Return an array containing all of the object's keys. +=head2 values + + r->table('marvel')->get('ironman')->values->run; + +Return an array containing all of an object’s values. C guarantees the +values will come out in the same order as L. + =head2 match r->table('users')->filter(sub { diff --git a/lib/Rethinkdb/Query/Database.pm b/lib/Rethinkdb/Query/Database.pm index 5b7dc02..ebe9352 100644 --- a/lib/Rethinkdb/Query/Database.pm +++ b/lib/Rethinkdb/Query/Database.pm @@ -106,6 +106,20 @@ sub table { return $t; } +sub grant { + my $self = shift; + my $user = shift; + my $perms = shift; + + my $q = Rethinkdb::Query->new( + _rdb => $self->_rdb, + _type => $self->_termType->grant, + args => [ $user, $perms ] + ); + + return $q; +} + sub config { my $self = shift; @@ -246,6 +260,13 @@ specified table doesn't exist a C is returned. List all table names in a database. The result is a list of strings. +=head2 grant + +r->db('test')->grant( 'username', { read => r->true, write => r->false } ) + ->run; + +Grant or deny access permissions for a user account on a database. + =head2 config r->db('test')->config->run; diff --git a/lib/Rethinkdb/Query/Table.pm b/lib/Rethinkdb/Query/Table.pm index db65281..17556f8 100644 --- a/lib/Rethinkdb/Query/Table.pm +++ b/lib/Rethinkdb/Query/Table.pm @@ -129,7 +129,7 @@ sub index_wait { } sub changes { - my $self = shift; + my $self = shift; my $params = shift; my $q = Rethinkdb::Query->new( @@ -272,6 +272,20 @@ sub get_nearest { return $q; } +sub grant { + my $self = shift; + my $user = shift; + my $perms = shift; + + my $q = Rethinkdb::Query->new( + _rdb => $self->_rdb, + _type => $self->_termType->grant, + args => [ $user, $perms ] + ); + + return $q; +} + sub config { my $self = shift; @@ -503,6 +517,13 @@ object of the requested geospatial index. Get all documents where the specified geospatial index is within a certain distance of the specified point (default 100 kilometers). +=head2 grant + +r->table('marvel')->grant( 'username', { read => r->true, write => r->false } ) + ->run; + +Grant or deny access permissions for a user account on a table. + =head2 config r->table('marvel')->config->run; diff --git a/t/admin.t b/t/admin.t index 8c63c96..bd2fa3d 100644 --- a/t/admin.t +++ b/t/admin.t @@ -99,8 +99,126 @@ r->table('marvel')->insert( ] )->run; +# create a few users +r->db('rethinkdb')->table('users')->insert( + [ + { + id => 'chatapp', + password => { password => 'chatapp-secret', iterations => 1024 } + }, + { + id => 'monitoring', + password => { password => 'monitoring-secret', iterations => 1024 } + }, + { + id => 'bob', + password => { password => 'bob-secret', iterations => 1024 } + } + ] +)->run; + my $res; +# grant global +$res = r->grant( 'chatapp', { read => r->true, write => r->true } )->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply [ sort keys %{ $res->response->{permissions_changes}->[0] } ], + [ 'new_val', 'old_val' ], 'Correct structure returned'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->true }, + 'old_val' => { 'write' => r->true, 'read' => r->true } + }, + 'Correct structure returned'; + +$res = r->grant( + 'monitoring', + { + read => r->true, + write => r->false, + connect => r->false, + config => r->false + } +)->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false }, + 'old_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false } + }, + 'Correct structure returned'; + +# grant database +$res = r->db('test')->grant( 'chatapp', { read => r->true, write => r->true } )->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply [ sort keys %{ $res->response->{permissions_changes}->[0] } ], + [ 'new_val', 'old_val' ], 'Correct structure returned'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->true }, + 'old_val' => { 'write' => r->true, 'read' => r->true } + }, + 'Correct structure returned'; + +$res = r->db('test')->grant( + 'monitoring', + { + read => r->true, + write => r->false, + connect => r->false, + config => r->false + } +)->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false }, + 'old_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false } + }, + 'Correct structure returned'; + +# grant table +$res = r->table('marvel')->grant( 'chatapp', { read => r->true, write => r->true } )->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply [ sort keys %{ $res->response->{permissions_changes}->[0] } ], + [ 'new_val', 'old_val' ], 'Correct structure returned'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->true }, + 'old_val' => { 'write' => r->true, 'read' => r->true } + }, + 'Correct structure returned'; + +$res = r->table('marvel')->grant( + 'monitoring', + { + read => r->true, + write => r->false, + connect => r->false, + config => r->false + } +)->run; + +is $res->type, 1, 'Correct response type'; +is $res->response->{granted}, 1, 'Correct response'; +is_deeply $res->response->{permissions_changes}->[0], + { + 'new_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false }, + 'old_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false } + }, + 'Correct structure returned'; + + # config - database $res = r->db('test')->config->run; @@ -113,7 +231,10 @@ $res = r->table('marvel')->config->run; is $res->type, 1, 'Correct response type'; is_deeply [ sort keys %{ $res->response } ], - [ 'db', 'durability', 'id', 'indexes', 'name', 'primary_key', 'shards', 'write_acks' ], + [ + 'db', 'durability', 'id', 'indexes', + 'name', 'primary_key', 'shards', 'write_acks' + ], 'Correct structure returned'; # rebalance - database @@ -179,12 +300,12 @@ is_deeply $res->response->{status}, $res = r->db('test')->wait->run; is $res->type, 1, 'Correct response type'; -is $res->response->{ready}, 1, 'Correct response type'; +is $res->response->{ready}, 1, 'Correct response type'; # wait - table $res = r->table('marvel')->wait->run; is $res->type, 1, 'Correct response type'; -is $res->response->{ready}, 1, 'Correct response type'; +is $res->response->{ready}, 1, 'Correct response type'; done_testing(); diff --git a/t/aggregation.t b/t/aggregation.t index 1b5da27..f13bd14 100644 --- a/t/aggregation.t +++ b/t/aggregation.t @@ -213,13 +213,54 @@ $res = r->table('marvel')->map( r->row->bracket('age') )->reduce( )->default(0)->run; is $res->type, 1, 'Correct response type'; -is $res->response, '1400', 'Correct number of documents'; +is $res->response, '1400', 'Correct response'; + +# fold +$res = r->table('marvel')->fold( + 0, + sub ($$) { + my ( $acc, $row ) = @_; + return $acc->add( $row->attr('age') ); + } +)->run; + +is $res->type, 1, 'Correct response type'; +is $res->response, '1400', 'Correct response'; + +$res = r->table('marvel')->fold( + [], + sub ($$) { + my ( $acc, $row ) = @_; + return $acc->append( $row->attr('age') ); + } +)->run; + +is $res->type, 1, 'Correct response type'; +is_deeply $res->response, [ 135, 35, 35, 35, 35, 1035, 35, 20, 35 ], + 'Correct response'; + +$res = r->table('marvel')->fold( + 0, + sub ($$) { + my ( $acc, $row ) = @_; + return $acc->add(1); + }, + sub ($$$) { + my ( $acc, $row, $newAcc ) = @_; + return r->branch( $acc->mod(2)->eq(0), [$row], [] ); + } +)->run; + +is $res->type, 2, 'Correct response type'; +is_deeply [ map { $_->{superhero} } @{ $res->response } ], + [ 'Captain America', 'Ant-Man', 'Hawk-Eye', 'Wasp', 'Iron Man' ], + 'Correct response'; # count $res = r->table('marvel')->count->run; is $res->type, 1, 'Correct response type'; -is $res->response, '9', 'Correct number of documents'; +is $res->response, '9', 'Correct response'; # count (with parameter) $res = r->table('marvel')->concat_map( @@ -230,7 +271,7 @@ $res = r->table('marvel')->concat_map( )->count('Batman')->run; is $res->type, 1, 'Correct response type'; -is $res->response, '4', 'Correct number of documents'; +is $res->response, '4', 'Correct response'; $res = r->table('marvel')->count( sub { @@ -240,7 +281,7 @@ $res = r->table('marvel')->count( )->run; is $res->type, 1, 'Correct response type'; -is $res->response, '4', 'Correct number of documents'; +is $res->response, '4', 'Correct response'; # sum $res = r->expr( [ 3, 5, 7 ] )->sum->run($conn); @@ -324,13 +365,13 @@ is $res->response->{age}, 1035, 'Correct response'; $res = r->table('marvel')->distinct->run; is $res->type, 2, 'Correct response type'; -is scalar @{ $res->response }, 9, 'Correct number of documents'; +is scalar @{ $res->response }, 9, 'Correct response'; # distinct (on query) $res = r->expr( [ 1, 1, 1, 1, 1, 2, 3 ] )->distinct->run($conn); is $res->type, 1, 'Correct response type'; -is scalar @{ $res->response }, 3, 'Correct number of documents'; +is scalar @{ $res->response }, 3, 'Correct response'; # contains $res = r->table('marvel')->get('Iron Man')->bracket('dc_buddies') diff --git a/t/connect.t b/t/connect.t index b3e5304..1518d91 100644 --- a/t/connect.t +++ b/t/connect.t @@ -138,38 +138,67 @@ r->db('test')->table('battle')->insert( )->run; # group_format -$res = r->db('test')->table('battle')->group('superhero')->run( { group_format => 'raw' } ); +$res = r->db('test')->table('battle')->group('superhero') + ->run( { group_format => 'raw' } ); -is $res->response->{'$reql_type$'}, 'GROUPED_DATA', 'Correct group_format response data'; +is $res->response->{'$reql_type$'}, 'GROUPED_DATA', + 'Correct group_format response data'; isa_ok $res->response->{data}, 'ARRAY', 'Correct group_format response data'; -isa_ok $res->response->{data}[0][1], 'ARRAY', 'Correct group_format response data'; +isa_ok $res->response->{data}[0][1], 'ARRAY', + 'Correct group_format response data'; # db -$res = r->table('cluster_config')->run({db => 'rethinkdb'}); -ok ($res->response->[0]->{id} eq 'auth' or $res->response->[0]->{id} eq 'heartbeat'), 'Correct response for db change'; +$res = r->table('cluster_config')->run( { db => 'rethinkdb' } ); +ok( + $res->response->[0]->{id} eq 'auth' + or $res->response->[0]->{id} eq 'heartbeat' + ), + 'Correct response for db change'; # array_limit (doesn't seem to change response) -r->db('test')->table('battle')->run({array_limit => 2}); +r->db('test')->table('battle')->run( { array_limit => 2 } ); # noreply -$res = r->db('test')->table('battle')->run({noreply => 1}); +$res = r->db('test')->table('battle')->run( { noreply => 1 } ); is $res, undef, 'Correct response for noreply'; # test a callback -$res = r->db('test')->table('battle')->run(sub { - my $res = shift; - isa_ok $res, 'Rethinkdb::Response', 'Correct response for callback'; -}); +$res = r->db('test')->table('battle')->run( + sub { + my $res = shift; + isa_ok $res, 'Rethinkdb::Response', 'Correct response for callback'; + } +); isa_ok $res, 'Rethinkdb::Response', 'Correct response for callback return'; # check default database parameter is being used -r->connect('localhost', 28015, 'random' . int(rand(1000)))->repl; +r->connect( 'localhost', 28015, 'random' . int( rand(1000) ) )->repl; $res = r->table('superheroes')->create->run; is $res->{error_type}, 4100000, 'Expected error_type'; like $res->{response}->[0], qr/Database `random[0-9]+` does not exist./; +# server information +$res = r->server; + +is $res->type, 5, 'Expected response type'; +is_deeply [ sort( keys %{ $res->response->[0] } ) ], + [ 'id', 'name', 'proxy' ], 'Correct response keys'; +like $res->response->[0]->{id}, + qr/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, + 'Correct response'; + +$conn = r->connect; +$res = $conn->server; + +is $res->type, 5, 'Expected response type'; +is_deeply [ sort( keys %{ $res->response->[0] } ) ], + [ 'id', 'name', 'proxy' ], 'Correct response keys'; +like $res->response->[0]->{id}, + qr/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, + 'Correct response'; + # clean up r->db('test')->drop->run; diff --git a/t/document.t b/t/document.t index 8ec4cfa..02e6a79 100644 --- a/t/document.t +++ b/t/document.t @@ -235,7 +235,7 @@ is $res->response, 'medium', 'Correct response'; $res = r->table('marvel')->get_field('superpower')->run; is $res->type, 2, 'Correct response type'; -is_deeply [sort @{$res->response}], +is_deeply [ sort @{ $res->response } ], [ 'Adamantium', 'Bio-lasers', 'Bow-n-arrow', 'God-like powers', 'Size', 'Smash', 'Spidy Sense', 'Super Strength', @@ -251,7 +251,7 @@ is $res->response, 'medium', 'Correct response'; $res = r->table('marvel')->bracket('superpower')->run; is $res->type, 2, 'Correct response type'; -is_deeply [sort @{$res->response}], +is_deeply [ sort @{ $res->response } ], [ 'Adamantium', 'Bio-lasers', 'Bow-n-arrow', 'God-like powers', 'Size', 'Smash', 'Spidy Sense', 'Super Strength', @@ -267,7 +267,7 @@ is $res->response, 'medium', 'Correct response'; $res = r->table('marvel')->attr('superpower')->run; is $res->type, 2, 'Correct response type'; -is_deeply [sort @{$res->response}], +is_deeply [ sort @{ $res->response } ], [ 'Adamantium', 'Bio-lasers', 'Bow-n-arrow', 'God-like powers', 'Size', 'Smash', 'Spidy Sense', 'Super Strength', @@ -300,8 +300,10 @@ is_deeply $res->response, # remove nested key(s) -$res = r->table('marvel')->get('Iron Man') - ->without( 'personalVictoriesList', 'equipment', { stuff => { laserCannons => r->true } } )->run; +$res + = r->table('marvel')->get('Iron Man') + ->without( 'personalVictoriesList', 'equipment', + { stuff => { laserCannons => r->true } } )->run; is $res->type, 1, 'Correct response type'; is_deeply $res->response, @@ -369,6 +371,21 @@ is_deeply $res->response, ], 'Correct keys'; +# Return an array containing all of an object’s values. values() guarantees +# the values will come out in the same order as keys. +$res = r->table('marvel')->get('Iron Man')->values->run; + +is $res->type, 1, 'Correct response type'; +is_deeply $res->response, + [ + 30, + [ 'oldBoots', 'oldHelm' ], + [ 'Fing Fang Foom', 'Iron Monger', 'Mandarin' ], + 4500, 'medium', { 'laserCannons' => 2, 'missels' => 12 }, + 'Iron Man' + ], + 'Correct keys'; + # literal r->table('marvel')->get('Iron Man')->update( { From defca2f7413cb24c012eec5d06503a6223517174 Mon Sep 17 00:00:00 2001 From: Nathan Levin-Greenhaw Date: Fri, 1 Jul 2016 10:48:06 -0700 Subject: [PATCH 3/4] test needs to clean itself up + test for the correct thing --- t/admin.t | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/t/admin.t b/t/admin.t index bd2fa3d..9e78e12 100644 --- a/t/admin.t +++ b/t/admin.t @@ -129,7 +129,7 @@ is_deeply [ sort keys %{ $res->response->{permissions_changes}->[0] } ], is_deeply $res->response->{permissions_changes}->[0], { 'new_val' => { 'read' => r->true, 'write' => r->true }, - 'old_val' => { 'write' => r->true, 'read' => r->true } + 'old_val' => undef }, 'Correct structure returned'; @@ -148,7 +148,7 @@ is $res->response->{granted}, 1, 'Correct response'; is_deeply $res->response->{permissions_changes}->[0], { 'new_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false }, - 'old_val' => { 'read' => r->true, 'write' => r->false, 'connect' => r->false, 'config' => r->false } + 'old_val' => undef }, 'Correct structure returned'; @@ -308,4 +308,10 @@ $res = r->table('marvel')->wait->run; is $res->type, 1, 'Correct response type'; is $res->response->{ready}, 1, 'Correct response type'; +# clean up +r->db('test')->drop->run; +r->db('rethinkdb')->table('users')->get('chatapp')->delete->run; +r->db('rethinkdb')->table('users')->get('monitoring')->delete->run; +r->db('rethinkdb')->table('users')->get('bob')->delete->run; + done_testing(); From 417aa38758f7b6130aa3fa5600223b59f99bb58d Mon Sep 17 00:00:00 2001 From: Nathan Levin-Greenhaw Date: Fri, 1 Jul 2016 11:26:01 -0700 Subject: [PATCH 4/4] updating changes for last release --- Changes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changes b/Changes index 6d940d9..d4e3eeb 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,8 @@ +0.12 2016-04-29 + - Fix for Issue #22 + - Fixing some bad tests + - Tested with RethinkDB v2.3.1 + 0.11 2015-09-17 - RethinkDB 2.1.0-1 compatibility - Geospatial functionality