Prechádzať zdrojové kódy

Merge pull request #164 from mpspahr/fix_plans_runs_filtering

Update methods that support filtering
George S. Baugh 2 rokov pred
rodič
commit
361661037d
2 zmenil súbory, kde vykonal 124 pridanie a 61 odobranie
  1. 117 53
      lib/TestRail/API.pm
  2. 7 8
      t/TestRail-API.t

+ 117 - 53
lib/TestRail/API.pm

@@ -457,21 +457,33 @@ sub deleteProject {
     return $self->_doRequest('index.php?/api/v2/delete_project/'.$proj,'POST');
 }
 
-=head2 B<getProjects ()>
+=head2 B<getProjects (filters)>
 
 Get all available projects
 
+=over 4
+
+=item HASHREF C<FILTERS> (optional) - HASHREF describing parameters to filter cases by.
+
+=back
+
 Returns array of project definition HASHREFs, false otherwise.
 
     $projects = $tl->getProjects;
 
+See:
+
+    L<https://www.gurock.com/testrail/docs/api/reference/projects#getprojects>
+
+for details as to the allowable filter keys.
+
 =cut
 
 sub getProjects {
-    state $check = compile(Object);
-    my ($self) = $check->(@_);
-
-    my $result = $self->_doRequest('index.php?/api/v2/get_projects');
+    state $check = compile(Object,Optional[Maybe[HashRef]]);
+    my ($self,$filters) = $check->(@_);
+    
+    my $result = $self->_doRequest('index.php?/api/v2/get_projects' . _convert_filters_to_string($filters) );
 
     #Save state for future use, if needed
     return -500 if !$result || (reftype($result) || 'undef') ne 'ARRAY';
@@ -1110,20 +1122,7 @@ sub getCases {
     my ($self,$project_id,$suite_id,$filters) = $check->(@_);
 
     my $url = "index.php?/api/v2/get_cases/$project_id&suite_id=$suite_id";
-
-    my @valid_keys = qw{section_id created_after created_before created_by milestone_id priority_id type_id updated_after updated_before updated_by};
-
-
-    # Add in filters
-    foreach my $filter (keys(%$filters)) {
-        confess("Invalid filter key '$filter' passed") unless grep {$_ eq $filter} @valid_keys;
-        if (ref $filters->{$filter} eq 'ARRAY') {
-            confess "$filter cannot be an ARRAYREF" if grep {$_ eq $filter} qw{created_after created_before updated_after updated_before};
-            $url .= "&$filter=".join(',',@{$filters->{$filter}});
-        } else {
-            $url .= "&$filter=".$filters->{$filter} if defined($filters->{$filter});
-        }
-    }
+    $url .= _convert_filters_to_string($filters);
 
     return $self->_doRequest($url);
 }
@@ -1364,7 +1363,7 @@ sub deleteRun {
     return $self->_doRequest("index.php?/api/v2/delete_run/$run_id",'POST');
 }
 
-=head2 B<getRuns (project_id)>
+=head2 B<getRuns (project_id,filters)>
 
 Get all runs for specified project.
 To do this, it must make (no. of runs/250) HTTP requests.
@@ -1374,32 +1373,54 @@ This is due to the maximum result set limit enforced by testrail.
 
 =item INTEGER C<PROJECT_ID> - ID of parent project
 
+=item HASHREF C<FILTERS> - (optional) dictionary of filters, with keys corresponding to the documented filters for get_runs (other than limit/offset).
+
 =back
 
 Returns ARRAYREF of run definition HASHREFs.
 
     $allRuns = $tr->getRuns(6969);
 
+Possible filters:
+
+=over 4
+
+=item created_after (UNIX timestamp)
+
+=item created_before (UNIX timestamp)
+
+=item created_by (csv of ints) IDs of users plans were created by
+
+=item is_completed (bool)
+
+=item milestone_id (csv of ints) IDs of milestone assigned to plans
+
+=item refs_filter (string) A single Reference ID (e.g. TR-a, 4291, etc.)
+
+=item suite_id (csv of ints) A comma-separated list of test suite IDs to filter by.
+
+=back
+
 =cut
 
 sub getRuns {
-    state $check = compile(Object, Int);
-    my ($self,$project_id) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[HashRef]]);
+    my ($self,$project_id,$filters) = $check->(@_);
 
-    my $initial_runs = $self->getRunsPaginated($project_id,$self->{'global_limit'},0);
+    my $initial_runs = $self->getRunsPaginated($project_id,$self->{'global_limit'},0,$filters);
     return $initial_runs unless (reftype($initial_runs) || 'undef') eq 'ARRAY';
     my $runs = [];
     push(@$runs,@$initial_runs);
     my $offset = 1;
     while (scalar(@$initial_runs) == $self->{'global_limit'}) {
-        $initial_runs = $self->getRunsPaginated($project_id,$self->{'global_limit'},($self->{'global_limit'} * $offset));
+        $initial_runs = $self->getRunsPaginated($project_id,$self->{'global_limit'},($self->{'global_limit'} * $offset),$filters);
         push(@$runs,@$initial_runs);
         $offset++;
     }
     return $runs;
 }
 
-=head2 B<getRunsPaginated (project_id,limit,offset)>
+=head2 B<getRunsPaginated (project_id,limit,offset,filters)>
 
 Get some runs for specified project.
 
@@ -1411,6 +1432,8 @@ Get some runs for specified project.
 
 =item INTEGER C<OFFSET> - Page of runs to return.
 
+=item HASHREF C<FILTERS> - (optional) other filters to apply to the requests other than limit/offset.  See getRuns for more information.
+
 =back
 
 Returns ARRAYREF of run definition HASHREFs.
@@ -1420,13 +1443,14 @@ Returns ARRAYREF of run definition HASHREFs.
 =cut
 
 sub getRunsPaginated {
-    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]]);
-    my ($self,$project_id,$limit,$offset) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]], Optional[Maybe[HashRef]]);
+    my ($self,$project_id,$limit,$offset,$filters) = $check->(@_);
 
     confess("Limit greater than ".$self->{'global_limit'}) if $limit > $self->{'global_limit'};
     my $apiurl = "index.php?/api/v2/get_runs/$project_id";
     $apiurl .= "&offset=$offset" if defined($offset);
     $apiurl .= "&limit=$limit" if $limit; #You have problems if you want 0 results
+    $apiurl .= _convert_filters_to_string($filters);
     return $self->_doRequest($apiurl);
 }
 
@@ -1568,34 +1592,35 @@ you will need to use getTestResults.
 =cut
 
 sub getRunResults {
-    state $check = compile(Object, Int);
-    my ($self,$run_id) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[HashRef]]);
+    my ($self,$run_id, $filters) = $check->(@_);
 
-    my $initial_results = $self->getRunResultsPaginated($run_id,$self->{'global_limit'},undef);
+    my $initial_results = $self->getRunResultsPaginated($run_id,$self->{'global_limit'},undef,$filters);
     return $initial_results unless (reftype($initial_results) || 'undef') eq 'ARRAY';
     my $results = [];
     push(@$results,@$initial_results);
     my $offset = 1;
     while (scalar(@$initial_results) == $self->{'global_limit'}) {
-        $initial_results = $self->getRunResultsPaginated($run_id,$self->{'global_limit'},($self->{'global_limit'} * $offset));
+        $initial_results = $self->getRunResultsPaginated($run_id,$self->{'global_limit'},($self->{'global_limit'} * $offset),$filters);
         push(@$results,@$initial_results);
         $offset++;
     }
     return $results;
 }
 
-=head2 B<getRunResultsPaginated(run_id,limit,offset)>
+=head2 B<getRunResultsPaginated(run_id,limit,offset,filters)>
 
 =cut
 
 sub getRunResultsPaginated {
-    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]]);
-    my ($self,$run_id,$limit,$offset) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]], Optional[Maybe[HashRef]]);
+    my ($self,$run_id,$limit,$offset,$filters) = $check->(@_);
 
     confess("Limit greater than ".$self->{'global_limit'}) if $limit > $self->{'global_limit'};
     my $apiurl = "index.php?/api/v2/get_results_for_run/$run_id";
     $apiurl .= "&offset=$offset" if defined($offset);
     $apiurl .= "&limit=$limit" if $limit; #You have problems if you want 0 results
+    $apiurl .= _convert_filters_to_string($filters);
     return $self->_doRequest($apiurl);
 }
 
@@ -1792,9 +1817,9 @@ Possible filters:
 
 sub getPlans {
     state $check = compile(Object, Int, Optional[Maybe[HashRef]]);
-    my ($self,$project_id, $filters) = $check->(@_);
+    my ($self,$project_id,$filters) = $check->(@_);
 
-    my $initial_plans = $self->getPlansPaginated($project_id,$self->{'global_limit'},0);
+    my $initial_plans = $self->getPlansPaginated($project_id,$self->{'global_limit'},0,$filters);
     return $initial_plans unless (reftype($initial_plans) || 'undef') eq 'ARRAY';
     my $plans = [];
     push(@$plans,@$initial_plans);
@@ -1819,7 +1844,7 @@ Get some plans for specified project.
 
 =item INTEGER C<OFFSET> - Page of plans to return.
 
-=item HASHREF C<FILTERS> - (optional) other filters to apply to the requests.  See getPlans for more information.
+=item HASHREF C<FILTERS> - (optional) other filters to apply to the requests (other than limit/offset).  See getPlans for more information.
 
 =back
 
@@ -1831,16 +1856,13 @@ Returns ARRAYREF of plan definition HASHREFs.
 
 sub getPlansPaginated {
     state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]], Optional[Maybe[HashRef]]);
-    my ($self,$project_id,$limit,$offset, $filters) = $check->(@_);
-    $filters //= {};
+    my ($self,$project_id,$limit,$offset,$filters) = $check->(@_);
 
     confess("Limit greater than ".$self->{'global_limit'}) if $limit > $self->{'global_limit'};
     my $apiurl = "index.php?/api/v2/get_plans/$project_id";
     $apiurl .= "&offset=$offset" if defined($offset);
     $apiurl .= "&limit=$limit" if $limit; #You have problems if you want 0 results
-    foreach my $key (keys(%$filters)) {
-        $apiurl .= "&$key=$filters->{$key}";
-    }
+    $apiurl .= _convert_filters_to_string($filters);
     return $self->_doRequest($apiurl);
 }
 
@@ -2078,7 +2100,7 @@ sub deleteMilestone {
     return $self->_doRequest("index.php?/api/v2/delete_milestone/$milestone_id",'POST');
 }
 
-=head2 B<getMilestones (project_id)>
+=head2 B<getMilestones (project_id,filters)>
 
 Get milestones for some project.
 
@@ -2086,20 +2108,27 @@ Get milestones for some project.
 
 =item INTEGER C<PROJECT ID> - ID of parent project.
 
+=item HASHREF C<FILTERS> (optional) - HASHREF describing parameters to filter milestones by.
+
 =back
 
+See:
+
+    L<https://www.gurock.com/testrail/docs/api/reference/milestones#getmilestones>
+
+for details as to the allowable filter keys.
+
 Returns ARRAYREF of milestone definition HASHREFs.
 
     $tr->getMilestones(8);
 
-
 =cut
 
 sub getMilestones {
-    state $check = compile(Object, Int);
-    my ($self,$project_id) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[HashRef]]);
+    my ($self,$project_id, $filters) = $check->(@_);
 
-    return $self->_doRequest("index.php?/api/v2/get_milestones/$project_id");
+    return $self->_doRequest("index.php?/api/v2/get_milestones/$project_id" . _convert_filters_to_string($filters));
 }
 
 =head2 B<getMilestoneByName (project_id,name)>
@@ -2501,7 +2530,7 @@ sub bulkAddResultsByCase {
 }
 
 
-=head2 B<getTestResults(test_id,limit,offset)>
+=head2 B<getTestResults(test_id,limit,offset,filters)>
 
 Get the recorded results for desired test, limiting output to 'limit' entries.
 
@@ -2513,23 +2542,32 @@ Get the recorded results for desired test, limiting output to 'limit' entries.
 
 =item INTEGER C<OFFSET> (OPTIONAL) - Offset to begin viewing result set at.
 
+=item HASHREF C<FILTERS> (optional) - HASHREF describing parameters to filter test results by (other than limit/offset).
+
 =back
 
+See:
+
+    L<https://www.gurock.com/testrail/docs/api/reference/results#getresults>
+
+for details as to the allowable filter keys.
+
 Returns ARRAYREF of result definition HASHREFs.
 
 =cut
 
 sub getTestResults {
-    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]]);
-    my ($self,$test_id,$limit,$offset) = $check->(@_);
+    state $check = compile(Object, Int, Optional[Maybe[Int]], Optional[Maybe[Int]], Optional[Maybe[HashRef]]);
+    my ($self,$test_id,$limit,$offset,$filters) = $check->(@_);
 
     my $url = "index.php?/api/v2/get_results/$test_id";
     $url .= "&limit=$limit" if $limit;
     $url .= "&offset=$offset" if defined($offset);
+    $url .= _convert_filters_to_string($filters);
     return $self->_doRequest($url);
 }
 
-=head2 B<getResultsForCase(run_id,case_id,limit,offset)>
+=head2 B<getResultsForCase(run_id,case_id,limit,offset,filters)>
 
 Get the recorded results for a test run and case combination., limiting output to 'limit' entries.
 
@@ -2543,19 +2581,28 @@ Get the recorded results for a test run and case combination., limiting output t
 
 =item INTEGER C<OFFSET> (OPTIONAL) - Offset to begin viewing result set at.
 
+=item HASHREF C<FILTERS> (optional) - HASHREF describing parameters to filter by (other than limit/offset).
+
 =back
 
+See:
+
+    L<https://www.gurock.com/testrail/docs/api/reference/results#getresultsforcase>
+
+for details as to the allowable filter keys.
+
 Returns ARRAYREF of result definition HASHREFs.
 
 =cut
 
 sub getResultsForCase {
-    state $check = compile(Object, Int, Int, Optional[Maybe[Int]], Optional[Maybe[Int]]);
-    my ($self,$run_id,$case_id,$limit,$offset) = $check->(@_);
+    state $check = compile(Object, Int, Int, Optional[Maybe[Int]], Optional[Maybe[Int]], Optional[Maybe[HashRef]]);
+    my ($self,$run_id,$case_id,$limit,$offset,$filters) = $check->(@_);
 
     my $url = "index.php?/api/v2/get_results_for_case/$run_id/$case_id";
     $url .= "&limit=$limit" if $limit;
     $url .= "&offset=$offset" if defined($offset);
+    $url .= _convert_filters_to_string($filters);
     return $self->_doRequest($url);
 }
 
@@ -2883,6 +2930,23 @@ sub buildStepResults {
     };
 }
 
+# Convenience method for building filter string from filters Hashref
+sub _convert_filters_to_string {
+    state $check = compile(Maybe[HashRef]);
+    my ($filters) = $check->(@_);
+
+    $filters //= {};
+
+    my $filter_string = '';
+    foreach my $filter (keys(%$filters)) {
+        if (ref $filters->{$filter} eq 'ARRAY') {
+            $filter_string .= "&$filter=".join(',',@{$filters->{$filter}});
+        } else {
+            $filter_string .= "&$filter=".$filters->{$filter} if defined($filters->{$filter});
+        }
+    }
+    return $filter_string;
+}
 
 1;
 

+ 7 - 8
t/TestRail-API.t

@@ -7,7 +7,7 @@ use lib "$FindBin::Bin/lib";
 use TestRail::API;
 use Test::LWP::UserAgent::TestRailMock;
 
-use Test::More tests => 94;
+use Test::More tests => 96;
 use Test::Fatal;
 use Test::Deep;
 use Scalar::Util ();
@@ -88,6 +88,7 @@ my $new_project = $tr->createProject($project_name,'Robo-Signed Soviet 5 Year Pr
 is($new_project->{'name'},$project_name,"Can create new project");
 
 ok($tr->getProjects(),"Get Projects returns list");
+ok($tr->getProjects({ is_completed => 1 }),"Get Projects returns a filtered list");
 is($tr->getProjectByName($project_name)->{'name'},$project_name,"Can get project by name");
 my $pjid = $tr->getProjectByID($new_project->{'id'});
 is(Scalar::Util::reftype($pjid) eq 'HASH' ? $pjid->{'id'} : $pjid,$new_project->{'id'},"Can get project by id");
@@ -127,19 +128,17 @@ my $case_filters = {
 };
 
 ok($tr->getCases($new_project->{'id'},$new_suite->{'id'},$case_filters),"Can get case listing");
+ok($tr->getCases($new_project->{'id'},$new_suite->{'id'},{'hokum' => 'bogus'}),"Passing bogus filter returns cases");
 is($tr->getCaseByName($new_project->{'id'}, $new_suite->{'id'},  $case_name, $case_filters)->{'title'},$case_name,"Can get case by name");
 is($tr->getCaseByID($new_case->{'id'})->{'id'},$new_case->{'id'},"Can get case by ID");
 
-#Negative case
-$case_filters->{'hokum'} = 'bogus';
-isnt(exception {$tr->getCases($new_project->{'id'},$new_suite->{'id'},$case_filters)},undef,"Passing bogus filter croaks");
-
 #Test RUN methods
 my $run_name = 'SEND T-1000 INFILTRATION UNITS BACK IN TIME';
 my $new_run = $tr->createRun($new_project->{'id'},$new_suite->{'id'},$run_name,"ACQUIRE CLOTHES, BOOTS AND MOTORCYCLE");
 is($new_run->{'name'},$run_name,"Can create new run");
 
-ok($tr->getRuns($new_project->{'id'}),"Can get list of runs");
+ok($tr->getRuns($new_project->{'id'}),"Can get list of all runs in a project");
+ok($tr->getRuns($new_project->{'id'},{is_completed => 1,milestone_id => 3}),"Can get list of runs, filtered by milestone ID & completion status");
 is($tr->getRunByName($new_project->{'id'},$run_name)->{'name'},$run_name,"Can get run by name");
 is($tr->getRunByID($new_run->{'id'})->{'id'},$new_run->{'id'},"Can get run by ID");
 
@@ -157,8 +156,8 @@ my $plan_name = "GosPlan";
 my $new_plan = $tr->createPlan($new_project->{'id'},$plan_name,"Soviet 5-year agriculture plan to liquidate Kulaks",$new_milestone->{'id'},[{ suite_id => $new_suite->{'id'}, name => "Executing the great plan"}]);
 is($new_plan->{'name'},$plan_name,"Can create new plan");
 
-ok($tr->getPlans($new_project->{'id'}),"Can get list of plans");
-ok($tr->getPlans($new_project->{'id'},{ is_completed => 1, milestone_id => 3}),"Can get list of plans, filtered by milestone ID & completion status");
+ok($tr->getPlans($new_project->{'id'}),"Can get list of all plans");
+ok($tr->getPlans($new_project->{'id'},{is_completed => 1,milestone_id => 3}),"Can get list of plans, filtered by milestone ID & completion status");
 my $namePlan = $tr->getPlanByName($new_project->{'id'},$plan_name);
 is($namePlan->{'name'},$plan_name,"Can get plan by name");
 is($tr->getPlanByID($new_plan->{'id'})->{'id'},$new_plan->{'id'},"Can get plan by ID");