Failed Conditions
Push — master ( 5433f0...117895 )
by Alexander
01:52
created

IssueCloner::createLinkedIssue()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 63
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 63
ccs 0
cts 56
cp 0
rs 7.2689
c 1
b 0
f 0
cc 7
eloc 40
nc 24
nop 5
crap 56

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the Jira-CLI library.
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @copyright Alexander Obuhovich <[email protected]>
8
 * @link      https://github.com/console-helpers/jira-cli
9
 */
10
11
namespace ConsoleHelpers\JiraCLI\Issue;
12
13
14
use chobie\Jira\Api;
15
use chobie\Jira\Issue;
16
use chobie\Jira\Issues\Walker;
17
18
class IssueCloner
19
{
20
21
	const LINK_DIRECTION_INWARD = 1;
22
23
	const LINK_DIRECTION_OUTWARD = 2;
24
25
	/**
26
	 * Jira REST client.
27
	 *
28
	 * @var Api
29
	 */
30
	protected $jiraApi;
31
32
	/**
33
	 * Specifies custom fields to copy during backporting.
34
	 *
35
	 * @var array
36
	 */
37
	private $_copyCustomFields = array(
38
		'Change Log Group', 'Change Log Message',
39
	);
40
41
	/**
42
	 * Custom fields map.
43
	 *
44
	 * @var array
45
	 */
46
	private $_customFieldsMap = array();
47
48
	/**
49
	 * Fields to query during issue search.
50
	 *
51
	 * @var array
52
	 */
53
	protected $queryFields = array('summary', 'issuelinks');
54
55
	/**
56
	 * IssueCloner constructor.
57
	 *
58
	 * @param Api $jira_api Jira REST client.
59
	 */
60
	public function __construct(Api $jira_api)
61
	{
62
		$this->jiraApi = $jira_api;
63
64
		$this->jiraApi->setOptions(0); // Don't expand fields.
65
	}
66
67
	/**
68
	 * Returns issues.
69
	 *
70
	 * @param string  $jql            JQL.
71
	 * @param string  $link_name      Link name.
72
	 * @param integer $link_direction Link direction.
73
	 *
74
	 * @return array
75
	 */
76
	public function getIssues($jql, $link_name, $link_direction)
77
	{
78
		$this->_buildCustomFieldsMap();
79
80
		$walker = new Walker($this->jiraApi);
81
		$walker->push($jql, implode(',', $this->_getQueryFields()));
82
83
		$ret = array();
84
85
		foreach ( $walker as $issue ) {
86
			$linked_issue = $this->_getLinkedIssue($issue, $link_name, $link_direction);
87
88
			if ( is_object($linked_issue) && $this->isAlreadyProcessed($issue, $linked_issue) ) {
89
				continue;
90
			}
91
92
			$ret[] = array($issue, $linked_issue);
93
		}
94
95
		return $ret;
96
	}
97
98
	/**
99
	 * Builds custom field map.
100
	 *
101
	 * @return void
102
	 */
103
	private function _buildCustomFieldsMap()
104
	{
105
		foreach ( $this->jiraApi->getFields() as $field_key => $field_data ) {
106
			if ( substr($field_key, 0, 12) === 'customfield_' ) {
107
				$this->_customFieldsMap[$field_data['name']] = $field_key;
108
			}
109
		}
110
	}
111
112
	/**
113
	 * Returns query fields.
114
	 *
115
	 * @return array
116
	 */
117
	private function _getQueryFields()
118
	{
119
		$ret = $this->queryFields;
120
121
		foreach ( $this->_copyCustomFields as $custom_field ) {
122
			if ( isset($this->_customFieldsMap[$custom_field]) ) {
123
				$ret[] = $this->_customFieldsMap[$custom_field];
124
			}
125
		}
126
127
		return $ret;
128
	}
129
130
	/**
131
	 * Returns issue, which backports given issue.
132
	 *
133
	 * @param Issue   $issue          Issue.
134
	 * @param string  $link_name      Link name.
135
	 * @param integer $link_direction Link direction.
136
	 *
137
	 * @return Issue|null
138
	 * @throws \InvalidArgumentException When link direction isn't valid.
139
	 */
140
	private function _getLinkedIssue(Issue $issue, $link_name, $link_direction)
141
	{
142
		foreach ( $issue->get('issuelinks') as $issue_link ) {
143
			if ( $issue_link['type']['name'] !== $link_name ) {
144
				continue;
145
			}
146
147
			if ( $link_direction === self::LINK_DIRECTION_INWARD ) {
148
				$check_key = 'inwardIssue';
149
			}
150
			elseif ( $link_direction === self::LINK_DIRECTION_OUTWARD ) {
151
				$check_key = 'outwardIssue';
152
			}
153
			else {
154
				throw new \InvalidArgumentException('The "' . $link_direction . '" link direction isn\'t valid.');
155
			}
156
157
			if ( array_key_exists($check_key, $issue_link) ) {
158
				$linked_issue = new Issue($issue_link[$check_key]);
159
160
				if ( $this->isLinkAccepted($issue, $linked_issue) ) {
161
					return $linked_issue;
162
				}
163
			}
164
		}
165
166
		return null;
167
	}
168
169
	/**
170
	 * Creates backports issues.
171
	 *
172
	 * @param Issue   $issue          Issue.
173
	 * @param string  $project_key    Project key.
174
	 * @param string  $link_name      Link name.
175
	 * @param integer $link_direction Link direction.
176
	 * @param array   $component_ids  Component IDs.
177
	 *
178
	 * @return string
179
	 * @throws \RuntimeException When failed to create an issue.
180
	 * @throws \InvalidArgumentException When link direction isn't valid.
181
	 */
182
	public function createLinkedIssue(Issue $issue, $project_key, $link_name, $link_direction, array $component_ids)
183
	{
184
		$create_fields = array(
185
			'description' => 'See ' . $issue->getKey() . '.',
186
			'components' => array(),
187
		);
188
189
		foreach ( $this->_copyCustomFields as $custom_field ) {
190
			if ( isset($this->_customFieldsMap[$custom_field]) ) {
191
				$custom_field_id = $this->_customFieldsMap[$custom_field];
192
				$create_fields[$custom_field_id] = $this->getIssueCustomField($issue, $custom_field_id);
193
			}
194
		}
195
196
		foreach ( $component_ids as $component_id ) {
197
			$create_fields['components'][] = array('id' => (string)$component_id);
198
		}
199
200
		$create_issue_result = $this->jiraApi->createIssue(
201
			$project_key,
202
			$issue->get('summary'),
1 ignored issue
show
Documentation introduced by
$issue->get('summary') is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
203
			$this->getChangelogEntryIssueTypeId(),
204
			$create_fields
205
		);
206
207
		$raw_create_issue_result = $create_issue_result->getResult();
208
209
		if ( array_key_exists('errors', $raw_create_issue_result) ) {
210
			throw new \RuntimeException(sprintf(
211
				'Failed to create linked issue for "%s" issue. Errors: ' . PHP_EOL . '%s',
212
				$issue->getKey(),
213
				print_r($raw_create_issue_result['errors'], true)
214
			));
215
		}
216
217
		if ( $link_direction === self::LINK_DIRECTION_INWARD ) {
218
			$issue_link_result = $this->jiraApi->api(
1 ignored issue
show
Unused Code introduced by
$issue_link_result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
219
				Api::REQUEST_POST,
220
				'/rest/api/2/issueLink',
221
				array(
222
					'type' => array('name' => $link_name),
223
					'inwardIssue' => array('key' => $raw_create_issue_result['key']),
224
					'outwardIssue' => array('key' => $issue->getKey()),
225
				)
226
			);
227
		}
228
		elseif ( $link_direction === self::LINK_DIRECTION_OUTWARD ) {
229
			$issue_link_result = $this->jiraApi->api(
1 ignored issue
show
Unused Code introduced by
$issue_link_result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
230
				Api::REQUEST_POST,
231
				'/rest/api/2/issueLink',
232
				array(
233
					'type' => array('name' => $link_name),
234
					'inwardIssue' => array('key' => $issue->getKey()),
235
					'outwardIssue' => array('key' => $raw_create_issue_result['key']),
236
				)
237
			);
238
		}
239
		else {
240
			throw new \InvalidArgumentException('The "' . $link_direction . '" link direction isn\'t valid.');
241
		}
242
243
		return $raw_create_issue_result['key'];
244
	}
245
246
	/**
247
	 * Determines if link was already processed.
248
	 *
249
	 * @param Issue $issue        Issue.
250
	 * @param Issue $linked_issue Linked issue.
251
	 *
252
	 * @return boolean
253
	 */
254
	protected function isAlreadyProcessed(Issue $issue, Issue $linked_issue)
255
	{
256
		return false;
257
	}
258
259
	/**
260
	 * Determines if link is accepted.
261
	 *
262
	 * @param Issue $issue        Issue.
263
	 * @param Issue $linked_issue Linked issue.
264
	 *
265
	 * @return boolean
266
	 */
267
	protected function isLinkAccepted(Issue $issue, Issue $linked_issue)
1 ignored issue
show
Unused Code introduced by
The parameter $issue is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
268
	{
269
		return true;
270
	}
271
272
	/**
273
	 * Returns ID of "Changelog Entry" issue type.
274
	 *
275
	 * @return integer
276
	 * @throws \LogicException When "Changelog Entry" issue type wasn't found.
277
	 */
278
	protected function getChangelogEntryIssueTypeId()
279
	{
280
		static $issue_type_id;
281
282
		if ( !isset($issue_type_id) ) {
283
			foreach ( $this->jiraApi->getIssueTypes() as $issue_type ) {
284
				if ( $issue_type->getName() === 'Changelog Entry' ) {
285
					$issue_type_id = $issue_type->getId();
286
					break;
287
				}
288
			}
289
290
			if ( !isset($issue_type_id) ) {
291
				throw new \LogicException('The "Changelog Entry" issue type not found.');
292
			}
293
		}
294
295
		return $issue_type_id;
296
	}
297
298
	/**
299
	 * Returns custom field value.
300
	 *
301
	 * @param Issue  $issue           Issue.
302
	 * @param string $custom_field_id Custom field ID.
303
	 *
304
	 * @return mixed
305
	 */
306
	protected function getIssueCustomField(Issue $issue, $custom_field_id)
307
	{
308
		$custom_field_data = $issue->get($custom_field_id);
309
310
		if ( is_array($custom_field_data) ) {
311
			return array('value' => $custom_field_data['value']);
312
		}
313
314
		return $custom_field_data;
315
	}
316
317
	/**
318
	 * Returns issue status name.
319
	 *
320
	 * @param Issue $issue Issue.
321
	 *
322
	 * @return string
323
	 */
324
	public function getIssueStatusName(Issue $issue)
325
	{
326
		$status = $issue->get('status');
327
328
		return $status['name'];
329
	}
330
331
}
332