Skip to content

Commit 35bb710

Browse files
authored
Merge pull request #408 from Art4/add-customfield-listnames
Add `CustomField::listNames()` method as replacement for `CustomField::listing()`
2 parents b118039 + f4bd056 commit 35bb710

File tree

9 files changed

+450
-10
lines changed

9 files changed

+450
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- New method `Redmine\Api\CustomField::listNames()` for listing the ids and names of all custom fields.
1213
- New method `Redmine\Api\Group::listNames()` for listing the ids and names of all groups.
1314
- New method `Redmine\Api\IssueCategory::listNamesByProject()` for listing the ids and names of all issue categories of a project.
1415

1516
### Deprecated
1617

18+
- `Redmine\Api\CustomField::listing()` is deprecated, use `\Redmine\Api\CustomField::listNames()` instead.
1719
- `Redmine\Api\Group::listing()` is deprecated, use `\Redmine\Api\Group::listNames()` instead.
1820
- `Redmine\Api\IssueCategory::listing()` is deprecated, use `\Redmine\Api\IssueCategory::listNamesByProject()` instead.
1921

src/Redmine/Api/CustomField.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class CustomField extends AbstractApi
1717
{
1818
private $customFields = [];
1919

20+
private $customFieldNames = null;
21+
2022
/**
2123
* List custom fields.
2224
*
@@ -37,6 +39,30 @@ final public function list(array $params = []): array
3739
}
3840
}
3941

42+
/**
43+
* Returns an array of all custom fields with id/name pairs.
44+
*
45+
* @return array<int,string> list of custom fields (id => name)
46+
*/
47+
final public function listNames(): array
48+
{
49+
if ($this->customFieldNames !== null) {
50+
return $this->customFieldNames;
51+
}
52+
53+
$this->customFieldNames = [];
54+
55+
$list = $this->list();
56+
57+
if (array_key_exists('custom_fields', $list)) {
58+
foreach ($list['custom_fields'] as $customField) {
59+
$this->customFieldNames[(int) $customField['id']] = (string) $customField['name'];
60+
}
61+
}
62+
63+
return $this->customFieldNames;
64+
}
65+
4066
/**
4167
* List custom fields.
4268
*
@@ -73,13 +99,18 @@ public function all(array $params = [])
7399
/**
74100
* Returns an array of custom fields with name/id pairs.
75101
*
102+
* @deprecated v2.7.0 Use listNames() instead.
103+
* @see CustomField::listNames()
104+
*
76105
* @param bool $forceUpdate to force the update of the custom fields var
77106
* @param array $params optional parameters to be passed to the api (offset, limit, ...)
78107
*
79108
* @return array list of custom fields (id => name)
80109
*/
81110
public function listing($forceUpdate = false, array $params = [])
82111
{
112+
@trigger_error('`' . __METHOD__ . '()` is deprecated since v2.7.0, use `' . __CLASS__ . '::listNames()` instead.', E_USER_DEPRECATED);
113+
83114
if (empty($this->customFields) || $forceUpdate) {
84115
$this->customFields = $this->list($params);
85116
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Redmine\Tests\Behat\Bootstrap;
6+
7+
use Redmine\Api\CustomField;
8+
9+
trait CustomFieldContextTrait
10+
{
11+
/**
12+
* @Given I create a custom field for issues with the name :customFieldName
13+
*/
14+
public function iCreateACustomFieldForIssuesWithTheName($customFieldName)
15+
{
16+
// support for creating custom fields via REST API is missing
17+
$this->redmine->excecuteDatabaseQuery(
18+
'INSERT INTO custom_fields(type, name, field_format, is_required, is_for_all, position) VALUES(:type, :name, :field_format, :is_required, :is_for_all, :position);',
19+
[],
20+
[
21+
':type' => 'IssueCustomField',
22+
':name' => $customFieldName,
23+
':field_format' => 'string',
24+
':is_required' => 0,
25+
':is_for_all' => 1,
26+
':position' => 1,
27+
],
28+
);
29+
}
30+
31+
/**
32+
* @Given I enable the tracker with ID :trackerId for custom field with ID :customFieldId
33+
*/
34+
public function iEnableTheTrackerWithIdForCustomFieldWithId($trackerId, $customFieldId)
35+
{
36+
// support for enabling custom fields for trackers via REST API is missing
37+
$this->redmine->excecuteDatabaseQuery(
38+
'INSERT INTO custom_fields_trackers(custom_field_id, tracker_id) VALUES(:custom_field_id, :tracker_id);',
39+
[],
40+
[
41+
':custom_field_id' => $customFieldId,
42+
':tracker_id' => $trackerId,
43+
],
44+
);
45+
}
46+
47+
/**
48+
* @When I list all custom fields
49+
*/
50+
public function iListAllCustomFields()
51+
{
52+
/** @var CustomField */
53+
$api = $this->getNativeCurlClient()->getApi('custom_fields');
54+
55+
$this->registerClientResponse(
56+
$api->list(),
57+
$api->getLastResponse(),
58+
);
59+
}
60+
61+
/**
62+
* @When I list all custom field names
63+
*/
64+
public function iListAllCustomFieldNames()
65+
{
66+
/** @var CustomField */
67+
$api = $this->getNativeCurlClient()->getApi('custom_fields');
68+
69+
$this->registerClientResponse(
70+
$api->listNames(),
71+
$api->getLastResponse(),
72+
);
73+
}
74+
}

tests/Behat/Bootstrap/FeatureContext.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
final class FeatureContext extends TestCase implements Context
2424
{
2525
use AttachmentContextTrait;
26+
use CustomFieldContextTrait;
2627
use GroupContextTrait;
2728
use IssueCategoryContextTrait;
2829
use IssueContextTrait;

tests/Behat/Bootstrap/IssueContextTrait.php

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ trait IssueContextTrait
1515
*/
1616
public function iCreateAnIssueWithTheFollowingData(TableNode $table)
1717
{
18-
$data = [];
19-
20-
foreach ($table as $row) {
21-
$data[$row['property']] = $row['value'];
22-
}
18+
$data = $this->prepareIssueData($table);
2319

2420
/** @var Issue */
2521
$api = $this->getNativeCurlClient()->getApi('issue');
@@ -35,11 +31,7 @@ public function iCreateAnIssueWithTheFollowingData(TableNode $table)
3531
*/
3632
public function iUpdateTheIssueWithIdAndTheFollowingData($issueId, TableNode $table)
3733
{
38-
$data = [];
39-
40-
foreach ($table as $row) {
41-
$data[$row['property']] = $row['value'];
42-
}
34+
$data = $this->prepareIssueData($table);
4335

4436
/** @var Issue */
4537
$api = $this->getNativeCurlClient()->getApi('issue');
@@ -105,4 +97,23 @@ public function iRemoveTheIssueWithId($issueId)
10597
$api->getLastResponse(),
10698
);
10799
}
100+
101+
private function prepareIssueData(TableNode $table): array
102+
{
103+
$data = [];
104+
105+
foreach ($table as $row) {
106+
$key = $row['property'];
107+
$value = $row['value'];
108+
109+
// Support for json in custom_fields
110+
if ($key === 'custom_fields') {
111+
$value = json_decode($value, true);
112+
}
113+
114+
$data[$key] = $value;
115+
}
116+
117+
return $data;
118+
}
108119
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
@custom_field
2+
Feature: Interacting with the REST API for custom fields
3+
In order to interact with REST API for custom fields
4+
As a user
5+
I want to make sure the Redmine server replies with the correct response
6+
7+
Scenario: Listing of zero custom fields
8+
Given I have a "NativeCurlClient" client
9+
When I list all custom fields
10+
Then the response has the status code "200"
11+
And the response has the content type "application/json"
12+
And the returned data has only the following properties
13+
"""
14+
custom_fields
15+
"""
16+
And the returned data "custom_fields" property is an array
17+
And the returned data "custom_fields" property contains "0" items
18+
19+
Scenario: Listing of multiple custom fields
20+
Given I have a "NativeCurlClient" client
21+
And I create a custom field for issues with the name "Note B"
22+
And I create a custom field for issues with the name "Note A"
23+
When I list all custom fields
24+
Then the response has the status code "200"
25+
And the response has the content type "application/json"
26+
And the returned data has only the following properties
27+
"""
28+
custom_fields
29+
"""
30+
And the returned data "custom_fields" property is an array
31+
And the returned data "custom_fields" property contains "2" items
32+
# field 'description' was added in Redmine 5.1.0, see https://www.redmine.org/issues/37617
33+
And the returned data "custom_fields.0" property contains the following data with Redmine version ">= 5.1.0"
34+
| property | value |
35+
| id | 1 |
36+
| name | Note B |
37+
| description | null |
38+
| customized_type | issue |
39+
| field_format | string |
40+
| regexp | |
41+
| min_length | null |
42+
| max_length | null |
43+
| is_required | false |
44+
| is_filter | false |
45+
| searchable | false |
46+
| multiple | false |
47+
| default_value | null |
48+
| visible | true |
49+
| trackers | [] |
50+
| roles | [] |
51+
But the returned data "custom_fields.0" property contains the following data with Redmine version "< 5.1.0"
52+
| property | value |
53+
| id | 1 |
54+
| name | Note B |
55+
| customized_type | issue |
56+
| field_format | string |
57+
| regexp | |
58+
| min_length | null |
59+
| max_length | null |
60+
| is_required | false |
61+
| is_filter | false |
62+
| searchable | false |
63+
| multiple | false |
64+
| default_value | null |
65+
| visible | true |
66+
| trackers | [] |
67+
| roles | [] |
68+
# field 'description' was added in Redmine 5.1.0, see https://www.redmine.org/issues/37617
69+
And the returned data "custom_fields.1" property contains the following data with Redmine version ">= 5.1.0"
70+
| property | value |
71+
| id | 2 |
72+
| name | Note A |
73+
| description | null |
74+
| customized_type | issue |
75+
| field_format | string |
76+
| regexp | |
77+
| min_length | null |
78+
| max_length | null |
79+
| is_required | false |
80+
| is_filter | false |
81+
| searchable | false |
82+
| multiple | false |
83+
| default_value | null |
84+
| visible | true |
85+
| trackers | [] |
86+
| roles | [] |
87+
But the returned data "custom_fields.1" property contains the following data with Redmine version "< 5.1.0"
88+
| property | value |
89+
| id | 2 |
90+
| name | Note A |
91+
| customized_type | issue |
92+
| field_format | string |
93+
| regexp | |
94+
| min_length | null |
95+
| max_length | null |
96+
| is_required | false |
97+
| is_filter | false |
98+
| searchable | false |
99+
| multiple | false |
100+
| default_value | null |
101+
| visible | true |
102+
| trackers | [] |
103+
| roles | [] |
104+
105+
Scenario: Listing of multiple custom field names
106+
Given I have a "NativeCurlClient" client
107+
And I create a custom field for issues with the name "Note B"
108+
And I create a custom field for issues with the name "Note A"
109+
When I list all custom field names
110+
Then the response has the status code "200"
111+
And the response has the content type "application/json"
112+
And the returned data is an array
113+
And the returned data contains "2" items
114+
And the returned data contains the following data
115+
| property | value |
116+
| 1 | Note B |
117+
| 2 | Note A |

tests/Behat/features/issue.feature

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,70 @@ Feature: Interacting with the REST API for issues
122122
| id | 1 |
123123
| name | Redmine Admin |
124124

125+
@custom_field
126+
Scenario: Creating an issue with custom field
127+
Given I have a "NativeCurlClient" client
128+
And I have an issue status with the name "New"
129+
And I have an issue priority with the name "Normal"
130+
And I have a tracker with the name "Defect" and default status id "1"
131+
And I create a project with name "Test Project" and identifier "test-project"
132+
And I create a custom field for issues with the name "Note"
133+
And I enable the tracker with ID "1" for custom field with ID "1"
134+
When I create an issue with the following data
135+
| property | value |
136+
| subject | issue subject |
137+
| project | Test Project |
138+
| tracker | Defect |
139+
| priority | Normal |
140+
| status | New |
141+
| custom_fields | [{"id":1,"value":"Note for custom field"}] |
142+
Then the response has the status code "201"
143+
And the response has the content type "application/xml"
144+
And the returned data is an instance of "SimpleXMLElement"
145+
And the returned data has only the following properties
146+
"""
147+
id
148+
project
149+
tracker
150+
status
151+
priority
152+
author
153+
subject
154+
description
155+
start_date
156+
due_date
157+
done_ratio
158+
is_private
159+
estimated_hours
160+
total_estimated_hours
161+
custom_fields
162+
created_on
163+
updated_on
164+
closed_on
165+
"""
166+
And the returned data has proterties with the following data
167+
| property | value |
168+
| id | 1 |
169+
| subject | issue subject |
170+
| description | [] |
171+
| due_date | [] |
172+
| done_ratio | 0 |
173+
| is_private | false |
174+
| estimated_hours | [] |
175+
| total_estimated_hours | [] |
176+
And the returned data "custom_fields.custom_field" property has only the following properties
177+
"""
178+
@attributes
179+
value
180+
"""
181+
And the returned data "custom_fields.custom_field.@attributes" property contains the following data
182+
| property | value |
183+
| id | 1 |
184+
| name | Note |
185+
And the returned data "custom_fields.custom_field" property contains the following data
186+
| property | value |
187+
| value | Note for custom field |
188+
125189
Scenario: Updating an issue
126190
Given I have a "NativeCurlClient" client
127191
And I have an issue status with the name "New"

0 commit comments

Comments
 (0)