Skip to content

Commit 64eddd5

Browse files
[11.9] Feature/groups issues (#714)
1 parent dbc36b5 commit 64eddd5

File tree

6 files changed

+276
-5
lines changed

6 files changed

+276
-5
lines changed

src/Api/Groups.php

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,119 @@ public function subgroups($group_id, array $parameters = [])
378378
return $this->get('groups/'.self::encodePath($group_id).'/subgroups', $resolver->resolve($parameters));
379379
}
380380

381+
/**
382+
* @param int|string $group_id
383+
* @param array $parameters {
384+
*
385+
* @var string $assignee_id Return issues assigned to the given user id. Mutually exclusive with assignee_username.
386+
* None returns unassigned issues. Any returns issues with an assignee.
387+
* @var string $assignee_username Return issues assigned to the given username. Similar to assignee_id and mutually exclusive with assignee_id.
388+
* In GitLab CE, the assignee_username array should only contain a single value. Otherwise, an invalid parameter error is returned.
389+
* @var int $author_id Return issues created by the given user id. Mutually exclusive with author_username.
390+
* Combine with scope=all or scope=assigned_to_me.
391+
* @var string $author_username Return issues created by the given username. Similar to author_id and mutually exclusive with author_id.
392+
* @var bool $confidential Filter confidential or public issues
393+
* @var \DateTimeInterface $created_after Return issues created after the given time (inclusive)
394+
* @var \DateTimeInterface $created_before Return issues created before the given time (inclusive)
395+
* @var int $iteration_id Return issues assigned to the given iteration ID. None returns issues that do not belong to an iteration. Any returns issues that belong to an iteration. Mutually exclusive with iteration_title.
396+
* @var string $iteration_title Return issues assigned to the iteration with the given title. Similar to iteration_id and mutually exclusive with iteration_id.
397+
* @var string $labels Comma-separated list of label names, issues must have all labels to be returned. None lists all issues with no labels. Any lists all issues with at least one label. No+Label (Deprecated) lists all issues with no labels. Predefined names are case-insensitive.
398+
* @var string $milestone The milestone title. None lists all issues with no milestone. Any lists all issues that have an assigned milestone.
399+
* @var string $my_reaction_emoji Return issues reacted by the authenticated user by the given emoji. None returns issues not given a reaction. Any returns issues given at least one reaction.
400+
* @var bool $non_archived Return issues from non archived projects. Default is true.
401+
* @var string $not Return issues that do not match the parameters supplied. Accepts: labels, milestone, author_id, author_username, assignee_id, assignee_username, my_reaction_emoji, search, in
402+
* @var string $order_by Return issues ordered by created_at, updated_at, priority, due_date, relative_position, label_priority, milestone_due, popularity, weight fields. Default is created_at
403+
* @var string $scope Return issues for the given scope: created_by_me, assigned_to_me or all. Defaults to all.
404+
* @var string $search Search group issues against their title and description
405+
* @var string $sort Return issues sorted in asc or desc order. Default is desc
406+
* @var string $state Return all issues or just those that are opened or closed
407+
* @var \DateTimeInterface $updated_after Return issues updated on or after the given time. Expected in ISO 8601 format (2019-03-15T08:00:00Z)
408+
* @var \DateTimeInterface $updated_before Return issues updated on or before the given time. Expected in ISO 8601 format (2019-03-15T08:00:00Z)
409+
* @var int $weight Return issues with the specified weight. None returns issues with no weight assigned. Any returns issues with a weight assigned.
410+
* @var bool $with_labels_details If true, the response returns more details for each label in labels field: :name, :color, :description, :description_html, :text_color. Default is false.
411+
* }
412+
*
413+
* @return mixed
414+
*/
415+
public function issues($group_id, array $parameters = [])
416+
{
417+
$resolver = $this->createOptionsResolver();
418+
$booleanNormalizer = function (Options $resolver, $value): string {
419+
return $value ? 'true' : 'false';
420+
};
421+
$datetimeNormalizer = function (Options $resolver, \DateTimeInterface $value): string {
422+
return $value->format('c');
423+
};
424+
425+
$resolver->setDefined('assignee_id');
426+
$resolver->setDefined('assignee_username')
427+
->setAllowedTypes('assignee_username', 'string');
428+
429+
$resolver->setDefined('author_id');
430+
$resolver->setDefined('author_username')
431+
->setAllowedTypes('author_username', 'string');
432+
433+
$resolver->setDefined('confidential')
434+
->setAllowedTypes('confidential', 'bool')
435+
->setNormalizer('confidential', $booleanNormalizer);
436+
437+
$resolver->setDefined('created_after')
438+
->setAllowedTypes('created_after', \DateTimeInterface::class)
439+
->setNormalizer('created_after', $datetimeNormalizer);
440+
$resolver->setDefined('created_before')
441+
->setAllowedTypes('created_before', \DateTimeInterface::class)
442+
->setNormalizer('created_before', $datetimeNormalizer);
443+
444+
$resolver->setDefined('updated_after')
445+
->setAllowedTypes('updated_after', \DateTimeInterface::class)
446+
->setNormalizer('updated_after', $datetimeNormalizer);
447+
$resolver->setDefined('updated_before')
448+
->setAllowedTypes('updated_before', \DateTimeInterface::class)
449+
->setNormalizer('updated_before', $datetimeNormalizer);
450+
451+
$resolver->setDefined('iteration_id');
452+
$resolver->setDefined('iteration_title')
453+
->setAllowedTypes('iteration_title', 'string');
454+
455+
$resolver->setDefined('labels')
456+
->setAllowedTypes('labels', 'string');
457+
458+
$resolver->setDefined('milestone')
459+
->setAllowedTypes('milestone', 'string');
460+
461+
$resolver->setDefined('my_reaction_emoji')
462+
->setAllowedTypes('my_reaction_emoji', 'string');
463+
464+
$resolver->setDefined('non_archived')
465+
->setAllowedTypes('non_archived', 'bool')
466+
->setNormalizer('non_archived', $booleanNormalizer);
467+
468+
$resolver->setDefined('not')
469+
->setAllowedTypes('not', 'string');
470+
471+
$resolver->setDefined('order_by')
472+
->setAllowedValues('order_by', ['created_at', 'updated_at']);
473+
$resolver->setDefined('sort')
474+
->setAllowedValues('sort', ['asc', 'desc']);
475+
476+
$resolver->setDefined('scope')
477+
->setAllowedTypes('scope', 'string');
478+
479+
$resolver->setDefined('search')
480+
->setAllowedTypes('search', 'string');
481+
482+
$resolver->setDefined('state')
483+
->setAllowedValues('state', [self::STATE_ALL, self::STATE_OPENED, self::STATE_CLOSED]);
484+
485+
$resolver->setDefined('weight');
486+
487+
$resolver->setDefined('with_labels_details')
488+
->setAllowedTypes('with_labels_details', 'bool')
489+
->setNormalizer('with_labels_details', $booleanNormalizer);
490+
491+
return $this->get('groups/'.self::encodePath($group_id).'/issues', $resolver->resolve($parameters));
492+
}
493+
381494
/**
382495
* @param int|string $group_id
383496
* @param array $parameters
@@ -618,6 +731,37 @@ public function mergeRequests($group_id, array $parameters = [])
618731
return $this->get('groups/'.self::encodePath($group_id).'/merge_requests', $resolver->resolve($parameters));
619732
}
620733

734+
/**
735+
* @param int|string $group_id
736+
* @param array $parameters {
737+
*
738+
* @var string $state Return opened, upcoming, current (previously started), closed, or all iterations.
739+
* Filtering by started state is deprecated starting with 14.1, please use current instead.
740+
* @var string $search return only iterations with a title matching the provided string
741+
* @var bool $include_ancestors Include iterations from parent group and its ancestors. Defaults to true.
742+
* }
743+
*
744+
* @return mixed
745+
*/
746+
public function iterations($group_id, array $parameters = [])
747+
{
748+
$resolver = $this->createOptionsResolver();
749+
$booleanNormalizer = function (Options $resolver, $value): string {
750+
return $value ? 'true' : 'false';
751+
};
752+
753+
$resolver->setDefined('state')
754+
->setAllowedValues('state', ['opened', 'upcoming', 'current', 'current (previously started)', 'closed', 'all'])
755+
;
756+
$resolver->setDefined('include_ancestors')
757+
->setAllowedTypes('include_ancestors', 'bool')
758+
->setNormalizer('include_ancestors', $booleanNormalizer)
759+
->setDefault('include_ancestors', true)
760+
;
761+
762+
return $this->get('groups/'.self::encodePath($group_id).'/iterations', $resolver->resolve($parameters));
763+
}
764+
621765
/**
622766
* @param int|string $group_id
623767
* @param array $parameters {

src/Api/Issues.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ class Issues extends AbstractApi
4141
* @var int[] $iids return only the issues having the given iid
4242
* @var string $order_by return requests ordered by created_at or updated_at fields (default is created_at)
4343
* @var string $sort return requests sorted in asc or desc order (default is desc)
44-
* @var bool $confidential filter confidential or public issues
44+
* @var bool $confidential filter confidential or public issues
4545
* @var string $search search issues against their title and description
46+
* @var int $assignee_id return issues assigned to the specified user id
47+
* @var int $iteration_id return issues assigned to the specified iteration id
48+
* @var string $iteration_title return issues assigned to the specified iteration title
4649
* }
4750
*
4851
* @return mixed
@@ -465,6 +468,8 @@ protected function createOptionsResolver(): OptionsResolver
465468
;
466469
$resolver->setDefined('labels');
467470
$resolver->setDefined('milestone');
471+
$resolver->setDefined('milestone_id')
472+
->setAllowedTypes('milestone_id', 'integer');
468473
$resolver->setDefined('with_labels_details')
469474
->setAllowedTypes('with_labels_details', 'bool')
470475
->setNormalizer('with_labels_details', $booleanNormalizer)
@@ -495,6 +500,12 @@ protected function createOptionsResolver(): OptionsResolver
495500
$resolver->setDefined('assignee_id')
496501
->setAllowedTypes('assignee_id', 'integer')
497502
;
503+
$resolver->setDefined('iteration_id')
504+
->setAllowedTypes('iteration_id', 'integer')
505+
;
506+
$resolver->setDefined('iteration_title')
507+
->setAllowedTypes('iteration_title', 'string')
508+
;
498509
$resolver->setDefined('weight')
499510
->setAllowedTypes('weight', 'integer')
500511
;

src/Api/Projects.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,37 @@ public function boards($project_id)
693693
return $this->get($this->getProjectPath($project_id, 'boards'));
694694
}
695695

696+
/**
697+
* @param int|string $project_id
698+
* @param array $parameters {
699+
*
700+
* @var string $state Return opened, upcoming, current (previously started), closed, or all iterations.
701+
* Filtering by started state is deprecated starting with 14.1, please use current instead.
702+
* @var string $search return only iterations with a title matching the provided string
703+
* @var bool $include_ancestors Include iterations from parent group and its ancestors. Defaults to true.
704+
* }
705+
*
706+
* @return mixed
707+
*/
708+
public function iterations($project_id, array $parameters = [])
709+
{
710+
$resolver = $this->createOptionsResolver();
711+
$booleanNormalizer = function (Options $resolver, $value): string {
712+
return $value ? 'true' : 'false';
713+
};
714+
715+
$resolver->setDefined('state')
716+
->setAllowedValues('state', ['opened', 'upcoming', 'current', 'current (previously started)', 'closed', 'all'])
717+
;
718+
$resolver->setDefined('include_ancestors')
719+
->setAllowedTypes('include_ancestors', 'bool')
720+
->setNormalizer('include_ancestors', $booleanNormalizer)
721+
->setDefault('include_ancestors', true)
722+
;
723+
724+
return $this->get('projects/'.self::encodePath($project_id).'/iterations', $resolver->resolve($parameters));
725+
}
726+
696727
/**
697728
* Gets a list of all discussion items for a single commit.
698729
*

tests/Api/GroupsTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,27 @@ public function shouldGetAllSubgroups(): void
365365
$this->assertEquals($expectedArray, $api->subgroups(1, ['page' => 1, 'per_page' => 10]));
366366
}
367367

368+
/**
369+
* @test
370+
*/
371+
public function shouldGetAllIssues(): void
372+
{
373+
$expectedArray = [
374+
['id' => 101, 'name' => 'An issue'],
375+
['id' => 102, 'name' => 'Another issue'],
376+
['id' => 103, 'name' => 'A third issue'],
377+
];
378+
379+
$api = $this->getApiMock();
380+
$api->expects($this->once())
381+
->method('get')
382+
->with('groups/1/issues', ['page' => 1, 'per_page' => 10])
383+
->will($this->returnValue($expectedArray))
384+
;
385+
386+
$this->assertEquals($expectedArray, $api->issues(1, ['page' => 1, 'per_page' => 10]));
387+
}
388+
368389
/**
369390
* @test
370391
*/
@@ -677,6 +698,38 @@ public function shouldGetAllGroupProjectsIncludingCustomAttributes(): void
677698
$this->assertEquals($expectedArray, $api->projects(1, ['with_custom_attributes' => true]));
678699
}
679700

701+
/**
702+
* @test
703+
*/
704+
public function shouldGetIterations(): void
705+
{
706+
$expectedArray = [
707+
[
708+
'id' => 5,
709+
'iid' => 2,
710+
'sequence' => 1,
711+
'group_id' => 123,
712+
'title' => '2022: Sprint 1',
713+
'description' => '',
714+
'state' => 3,
715+
'created_at' => '2021-09-29T21:24:43.913Z',
716+
'updated_at' => '2022-03-29T19:09:08.368Z',
717+
'start_date' => '2022-01-10',
718+
'due_date' => '2022-01-23',
719+
'web_url' => 'https://example.com/groups/example/-/iterations/34',
720+
],
721+
];
722+
723+
$api = $this->getApiMock();
724+
$api->expects($this->once())
725+
->method('get')
726+
->with('groups/1/iterations')
727+
->will($this->returnValue($expectedArray))
728+
;
729+
730+
$this->assertEquals($expectedArray, $api->iterations(1));
731+
}
732+
680733
/**
681734
* @test
682735
*/

tests/Api/IssuesTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ public function shouldGetGroupIssuesWithParams(): void
9191
$api = $this->getApiMock();
9292
$api->expects($this->once())
9393
->method('get')
94-
->with('groups/1/issues', ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened'])
94+
->with('groups/1/issues', ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened', 'iteration_title' => 'Title', 'assignee_id' => 1])
9595
->will($this->returnValue($expectedArray))
9696
;
9797

98-
$this->assertEquals($expectedArray, $api->group(1, ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened']));
98+
$this->assertEquals($expectedArray, $api->group(1, ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened', 'iteration_title' => 'Title', 'assignee_id' => 1]));
9999
}
100100

101101
/**
@@ -131,11 +131,11 @@ public function shouldGetProjectIssuesWithParams(): void
131131
$api = $this->getApiMock();
132132
$api->expects($this->once())
133133
->method('get')
134-
->with('projects/1/issues', ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened'])
134+
->with('projects/1/issues', ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened', 'iteration_id' => 1, 'assignee_id' => 2])
135135
->will($this->returnValue($expectedArray))
136136
;
137137

138-
$this->assertEquals($expectedArray, $api->all(1, ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened']));
138+
$this->assertEquals($expectedArray, $api->all(1, ['order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'opened', 'iteration_id' => 1, 'assignee_id' => 2]));
139139
}
140140

141141
/**

tests/Api/ProjectsTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,38 @@ public function getProjectBoardsExpectedArray()
673673
];
674674
}
675675

676+
/**
677+
* @test
678+
*/
679+
public function shouldGetIterations(): void
680+
{
681+
$expectedArray = [
682+
[
683+
'id' => 5,
684+
'iid' => 2,
685+
'sequence' => 1,
686+
'group_id' => 123,
687+
'title' => '2022: Sprint 1',
688+
'description' => '',
689+
'state' => 3,
690+
'created_at' => '2021-09-29T21:24:43.913Z',
691+
'updated_at' => '2022-03-29T19:09:08.368Z',
692+
'start_date' => '2022-01-10',
693+
'due_date' => '2022-01-23',
694+
'web_url' => 'https://example.com/groups/example/-/iterations/34',
695+
],
696+
];
697+
698+
$api = $this->getApiMock();
699+
$api->expects($this->once())
700+
->method('get')
701+
->with('projects/1/iterations')
702+
->will($this->returnValue($expectedArray))
703+
;
704+
705+
$this->assertEquals($expectedArray, $api->iterations(1));
706+
}
707+
676708
/**
677709
* @test
678710
*/

0 commit comments

Comments
 (0)