Test Failed
Pull Request — master (#48)
by Brian
03:17
created

JiraProject::buildSubmittedIssue()   F

Complexity

Conditions 12
Paths 385

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 12.667

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 30
c 2
b 0
f 0
nc 385
nop 2
dl 0
loc 53
ccs 20
cts 24
cp 0.8333
crap 12.667
rs 3.8208

How to fix   Long Method    Complexity   

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
declare(strict_types=1);
3
4
/**
5
 * JiraProjectReader
6
 */
7
8
namespace Fr3nch13\Jira\Lib;
9
10
use Cake\Core\Configure;
11
use Fr3nch13\Jira\Exception\IssueSubmissionException;
12
use Fr3nch13\Jira\Exception\MissingAllowedTypeException;
13
use Fr3nch13\Jira\Exception\MissingConfigException;
14
use Fr3nch13\Jira\Exception\MissingDataException;
15
use Fr3nch13\Jira\Exception\MissingIssueException;
16
use Fr3nch13\Jira\Exception\MissingIssueFieldException;
17
use Fr3nch13\Jira\Exception\MissingProjectException;
18
use JiraRestApi\Configuration\ArrayConfiguration;
19
use JiraRestApi\Issue\Issue;
20
use JiraRestApi\Issue\IssueField;
21
use JiraRestApi\Issue\IssueService;
22
use JiraRestApi\Issue\JqlQuery;
23
use JiraRestApi\JiraException;
24
use JiraRestApi\Project\ProjectService;
25
26
/**
27
 * Jira Project class
28
 */
29
class JiraProject
30
{
31
    /**
32
     * @var \JiraRestApi\Configuration\ArrayConfiguration Config Object.
33
     */
34
    public $ConfigObj;
35
36
    /**
37
     * @var null|string The key for the project.
38
     */
39
    public $projectKey = null;
40
41
    /**
42
     * @var \JiraRestApi\Project\ProjectService The project service object.
43
     */
44
    public $ProjectService;
45
46
    /**
47
     * @var \JiraRestApi\Project\Project The project object.
48
     */
49
    protected $Project;
50
51
    /**
52
     * @var array<\JiraRestApi\Issue\Version> The list of a Project's Versions.
53
     */
54
    protected $Versions;
55
56
    /**
57
     * @var \JiraRestApi\Issue\IssueService The project service object.
58
     */
59
    public $IssueService;
60
61
    /**
62
     * @var array<string, mixed> The Cached list of issues.
63
     */
64
    protected $Issues = [];
65
66
    /**
67
     * @var array<string, mixed> The cached list of returned issue info from the below getIssue() method.
68
     */
69
    protected $issuesCache = [];
70
71
    /**
72
     * Valid Types.
73
     * Used to ensure we're getting a valid type when filtering.
74
     * Currently only support Jira Core and Software.
75
     *
76
     * @see https://confluence.atlassian.com/adminjiracloud/issue-types-844500742.html
77
     * @var array<int, string>
78
     */
79
    protected $validTypes = [
80
        'Bug',
81
        'Epic',
82
        'Story',
83
        'Subtask',
84
        'Task',
85
    ];
86
87
    /**
88
     * @var array<string, array<mixed>> Types of issues allowed to be submitted.
89
     */
90
    protected $allowedTypes = [
91
        'Task' => [
92
            'jiraType' => 'Task', // Must be one of the types in the $this->validTypes.
93
            'jiraLabels' => 'task-submitted', // The label used to tag user submitted bugs.
94
            // The form's field information.
95
            'formData' => [
96
                'fields' => [
97
                    'summary' => [
98
                        'type' => 'text',
99
                        'required' => true,
100
                    ],
101
                    'details' => [
102
                        'type' => 'textarea',
103
                        'required' => true,
104
                    ],
105
                ],
106
            ],
107
        ],
108
        'Bug' => [
109
            'jiraType' => 'Bug', // Must be one of the types in the $this->validTypes.
110
            'jiraLabels' => 'bug-submitted', // The label used to tag user submitted bugs.
111
            // The form's field information.
112
            'formData' => [
113
                'fields' => [
114
                    'summary' => [
115
                        'type' => 'text',
116
                        'required' => true,
117
                    ],
118
                    'details' => [
119
                        'type' => 'textarea',
120
                        'required' => true,
121
                    ],
122
                ],
123
            ],
124
        ],
125
        'FeatureRequest' => [
126
            'jiraType' => 'Story', // Must be one of the types in the $this->validTypes.
127
            'jiraLabels' => 'feature-request', // The label used to tag feature requests.
128
            // The form's field information.
129
            'formData' => [
130
                'fields' => [
131
                    'summary' => [
132
                        'type' => 'text',
133
                        'required' => true,
134
                    ],
135
                    'details' => [
136
                        'type' => 'textarea',
137
                        'required' => true,
138
                    ],
139
                ],
140
            ],
141
        ],
142
    ];
143
144
    /**
145
     * This is here for the Form object (or any other object) to use.
146
     * It tacks all errors, even if an exception is thrown.
147
     *
148
     * @var array<int|string, string>
149
     */
150
    protected $errors = [];
151
152
    /**
153
     * Constructor
154
     *
155
     * Reads the configuration, and crdate a config object to be passed to the other objects.
156
     *
157
     * @throws \Fr3nch13\Jira\Exception\MissingProjectException When the project can't be found.
158
     * @return void
159
     */
160 65
    public function __construct()
161
    {
162 65
        $this->configure();
163
164
        // setup the objects
165 65
        $this->ProjectService = new ProjectService($this->ConfigObj);
166
        try {
167 65
            $this->Project = $this->ProjectService->get($this->projectKey);
168
        } catch (JiraException $e) {
169
            $msg = $e->getMessage();
170
            if ($msg) {
171
                $msg = $this->extractJiraError($msg);
172
            }
173 65
            $msg = __('{0}: {1}', [
174 65
                $this->projectKey,
175
                $msg,
176
            ]);
177
            $this->setJiraError($msg, 'MissingProjectException');
178
            throw new MissingProjectException($msg);
179
        }
180
181
        $this->Versions = (array)$this->ProjectService->getVersions($this->projectKey);
182
        $this->IssueService = new IssueService($this->ConfigObj);
183
    }
184 65
185
    /**
186 65
     * Configures the object.
187 65
     * Broken out of construct.
188 1
     *
189 1
     * @throws \Fr3nch13\Jira\Exception\MissingConfigException When a config setting isn't set.
190
     * @return void
191 65
     */
192 65
    public function configure(): void
193 1
    {
194 1
        $schema = Configure::read('Jira.schema');
195
        if (!$schema) {
196 65
            $this->setJiraError('schema', 'MissingConfigException');
197 65
            throw new MissingConfigException('schema');
198 1
        }
199 1
        $host = Configure::read('Jira.host');
200
        if (!$host) {
201 65
            $this->setJiraError('host', 'MissingConfigException');
202 65
            throw new MissingConfigException('host');
203 1
        }
204 1
        $username = Configure::read('Jira.username');
205
        if (!$username) {
206 65
            $this->setJiraError('username', 'MissingConfigException');
207 65
            throw new MissingConfigException('username');
208 1
        }
209 1
        $apiKey = Configure::read('Jira.apiKey');
210
        if (!$apiKey) {
211 65
            $this->setJiraError('apiKey', 'MissingConfigException');
212 65
            throw new MissingConfigException('apiKey');
213 1
        }
214 1
        $projectKey = Configure::read('Jira.projectKey');
215
        if (!$projectKey) {
216 65
            $this->setJiraError('projectKey', 'MissingConfigException');
217 65
            throw new MissingConfigException('projectKey');
218 1
        }
219 1
        $useV3RestApi = Configure::read('Jira.useV3RestApi');
220
        if (!$useV3RestApi) {
221 65
            $this->setJiraError('useV3RestApi', 'MissingConfigException');
222
            throw new MissingConfigException('useV3RestApi');
223
        }
224
        $jiraLogFile = Configure::read('Jira.jiraLogFile');
225
        if (!$jiraLogFile) {
226
            $this->setJiraError('jiraLogFile', 'MissingConfigException');
227
            throw new MissingConfigException('jiraLogFile');
228
        }
229 65
        $this->ConfigObj = new ArrayConfiguration([
230
            'jiraHost' => $schema . '://' . $host,
231
            'jiraUser' => $username,
232
            'jiraPassword' => $apiKey,
233
            'useV3RestApi' => $useV3RestApi,
234
            'jiraLogFile' => $jiraLogFile,
235
        ]);
236
237
        $this->projectKey = $projectKey;
238 9
    }
239
240 9
    /**
241
     * Get the Project's Info.
242
     *
243
     * @return \JiraRestApi\Project\Project The information about the project.
244
     * @throws \Fr3nch13\Jira\Exception\MissingProjectException If the project can't be found.
245
     */
246
    public function getInfo(): \JiraRestApi\Project\Project
247
    {
248 2
        return $this->Project;
249
    }
250 2
251
    /**
252
     * Get the Project's Versions.
253
     *
254
     * @return array<\JiraRestApi\Issue\Version> A list of version objects.
255
     */
256
    public function getVersions(): array
257
    {
258
        return $this->Versions;
259 4
    }
260
261 4
    /**
262 4
     * Get the Project's Issues.
263 2
     *
264
     * @param string|null $type Filter the Issues by type.
265 4
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
266 4
     */
267
    public function getIssues(?string $type = null): \JiraRestApi\Issue\IssueSearchResult
268 4
    {
269 4
        $cacheKey = 'all';
270 2
        if ($type) {
271
            $cacheKey .= '-' . $type;
272 4
        }
273
        if (!isset($this->Issues[$cacheKey])) {
274 4
            $jql = new JqlQuery();
275
276
            $jql->setProject($this->projectKey);
277 4
            if ($type && in_array($type, $this->validTypes)) {
278
                $jql->setType($type);
279
            }
280
            $jql->addAnyExpression('ORDER BY key DESC');
281
282
            $this->Issues[$cacheKey] = $this->IssueService->search($jql->getQuery(), 0, 1000);
283
        }
284
285
        return $this->Issues[$cacheKey];
286 4
    }
287
288 4
    /**
289 4
     * Get the Project's Open Issues.
290 2
     *
291
     * @param string|null $type Filter the Issues by type.
292 4
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
293 4
     */
294
    public function getOpenIssues(?string $type = null): \JiraRestApi\Issue\IssueSearchResult
295 4
    {
296 4
        $cacheKey = 'open';
297 2
        if ($type) {
298
            $cacheKey .= '-' . $type;
299 4
        }
300 4
        if (!isset($this->Issues[$cacheKey])) {
301
            $jql = new JqlQuery();
302 4
303
            $jql->setProject($this->projectKey);
304
            if ($type && in_array($type, $this->validTypes)) {
305 4
                $jql->setType($type);
306
            }
307
            $jql->addAnyExpression('AND resolution is EMPTY');
308
            $jql->addAnyExpression('ORDER BY key DESC');
309
310
            $this->Issues[$cacheKey] = $this->IssueService->search($jql->getQuery(), 0, 1000);
311
        }
312
313
        return $this->Issues[$cacheKey];
314
    }
315
316 4
    /**
317
     * Gets info on a particular issue within your project.
318 4
     *
319 1
     * @param int|null $id The issue id. The integer part without the project key.
320 1
     * @throws \Fr3nch13\Jira\Exception\MissingDataException If the issue's id isn't given.
321
     * @throws \Fr3nch13\Jira\Exception\MissingIssueException If the project's issue can't be found.
322 3
     * @return \JiraRestApi\Issue\Issue|\JiraRestApi\Issue\IssueV3 the object that has the info of that issue.
323 3
     */
324
    public function getIssue(?int $id = null): Issue
325 3
    {
326 1
        if (!is_int($id)) {
327 1
            $this->setJiraError(__('Missing the Issue\'s ID.'), 'Exception');
328 1
            throw new MissingDataException(__('Missing the Issue\'s ID.'));
329
        }
330
        $key = $this->projectKey . '-' . $id;
331
        if (!isset($this->issuesCache[$key])) {
332 2
            try {
333
                $this->issuesCache[$key] = $this->IssueService->get($key);
334
            } catch (JiraException $e) {
335
                $msg = $e->getMessage();
336
                if ($msg) {
337
                    $msg = $this->extractJiraError($msg);
338
                }
339
                $msg = __('{0}: {1}', [
340 2
                    $key,
341
                    $msg,
342 2
                ]);
343
                $this->setJiraError($msg, 'MissingIssueException');
344
                throw new MissingIssueException($msg);
345
            }
346
        }
347
348
        return $this->issuesCache[$key];
349
    }
350 2
351
    /**
352 2
     * Gets a list of issues that are considered bugs.
353
     *
354
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
355
     */
356
    public function getBugs(): \JiraRestApi\Issue\IssueSearchResult
357
    {
358
        return $this->getIssues('Bug');
359
    }
360
361
    /**
362
     * Gets a list of open issues that are considered bugs.
363
     *
364
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
365
     */
366 29
    public function getOpenBugs(): \JiraRestApi\Issue\IssueSearchResult
367
    {
368 29
        return $this->getOpenIssues('Bug');
369 8
    }
370 1
371 1
    /**
372
     * Methods used to submit an Issue to Jira.
373
     */
374 7
375
    /**
376
     * Returns the allowed types and their settings
377 26
     *
378
     * @param string|null $type The type of issue you want to get.
379
     * @throws \Fr3nch13\Jira\Exception\MissingAllowedTypeException If a type is given, and that type is not configured.
380
     * @return array<string, mixed> the content of $this->allowedTypes.
381
     */
382
    public function getAllowedTypes(?string $type = null): array
383
    {
384
        if ($type) {
385
            if (!isset($this->allowedTypes[$type])) {
386
                $this->setJiraError($type, 'MissingAllowedTypeException');
387
                throw new MissingAllowedTypeException($type);
388 16
            }
389
390 16
            return $this->allowedTypes[$type];
391 16
        }
392 16
393 1
        return $this->allowedTypes;
394 1
    }
395
396 15
    /**
397 1
     * Allows you to modify the form allowdTypes to fir your situation.
398 1
     *
399
     * @param string $type The type of issue you want to add/modify.
400 14
     * @param array<string, mixed> $settings The settings for the type.
401 1
     * @throws \Fr3nch13\Jira\Exception\MissingIssueFieldException If we're adding a new issue type, and the summary field isn't defined.
402 1
     * @return void
403
     */
404 13
    public function modifyAllowedTypes(string $type, array $settings = []): void
405 1
    {
406 1
        if (!isset($this->allowedTypes[$type])) {
407
            $this->allowedTypes[$type] = [];
408
            if (!isset($settings['jiraType'])) {
409
                $this->setJiraError('jiraType', 'MissingIssueFieldException');
410 12
                throw new MissingIssueFieldException('jiraType');
411
            }
412
            if (!isset($settings['formData'])) {
413
                $this->setJiraError('formData', 'MissingIssueFieldException');
414
                throw new MissingIssueFieldException('formData');
415
            }
416
            if (!isset($settings['formData']['fields'])) {
417
                $this->setJiraError('formData.fields', 'MissingIssueFieldException');
418
                throw new MissingIssueFieldException('formData.fields');
419 32
            }
420
            if (!isset($settings['formData']['fields']['summary'])) {
421 32
                $this->setJiraError('formData.fields.summary', 'MissingIssueFieldException');
422
                throw new MissingIssueFieldException('formData.fields.summary');
423
            }
424
        }
425
426
        $this->allowedTypes[$type] += $settings;
427
    }
428
429
    /**
430
     * Checks to see if a type is allowed.
431
     *
432 27
     * @param string $type The type to check.
433
     * @return bool if it's allowed or not.
434 27
     */
435 1
    public function isAllowedType(string $type): bool
436 1
    {
437
        return isset($this->allowedTypes[$type]) ? true : false;
438
    }
439 26
440 1
    /**
441 1
     * Gets the array for the forms when submitting an issue to Jira.
442
     *
443
     * @param string|null $type The type of issue we're submitting.
444 25
     * @throws \Fr3nch13\Jira\Exception\MissingAllowedTypeException If that type is not configured.
445
     * @throws \Fr3nch13\Jira\Exception\MissingDataException If the form data for that type is missing.
446 25
     * @return array<string, mixed> The array of data to fill in the form with.
447
     */
448
    public function getFormData(?string $type = null): array
449
    {
450
        if (!$type) {
451 25
            $this->setJiraError('[$type is not set]', 'MissingAllowedTypeException');
452
            throw new MissingAllowedTypeException('[$type is not set]');
453
        }
454
455
        if (!$this->isAllowedType($type)) {
456 25
            $this->setJiraError($type, 'MissingAllowedTypeException');
457
            throw new MissingAllowedTypeException($type);
458
        }
459
460
        $allowedTypes = $this->getAllowedTypes();
461
462
        if (!isset($allowedTypes[$type]['formData'])) {
463
            $this->setJiraError('No form data is set.', 'MissingDataException');
464
            throw new MissingDataException(__('No form data is set.'));
465
        }
466
467
        if (!isset($allowedTypes[$type]['formData']['fields'])) {
468
            $this->setJiraError('No form data fields are set.', 'MissingDataException');
469 26
            throw new MissingDataException(__('No form data fields are set.'));
470
        }
471 26
472 1
        return $allowedTypes[$type]['formData'];
473 1
    }
474
475
    /**
476 25
     * Sets the formData variable if you want to modify the default/initial values.
477 1
     *
478 1
     * @param string $type The type you want to set the data for.
479
     *  - Needs to be in the allowedTypes already.
480
     * @param array<string, mixed> $data The definition of the allowed types
481 24
     * @throws \Fr3nch13\Jira\Exception\MissingAllowedTypeException If that type is not configured.
482
     * @throws \Fr3nch13\Jira\Exception\MissingDataException Uf the fields aren't defined.
483
     * @return void
484
     */
485
    public function setFormData(string $type, array $data = []): void
486
    {
487
        if (!$this->isAllowedType($type)) {
488
            $this->setJiraError($type, 'MissingAllowedTypeException');
489
            throw new MissingAllowedTypeException($type);
490
        }
491
492
        if (!isset($data['fields'])) {
493
            $this->setJiraError('No form data fields are set.', 'MissingDataException');
494
            throw new MissingDataException(__('No form data fields are set.'));
495 8
        }
496
497 8
        $this->allowedTypes[$type]['formData'] = $data;
498 1
    }
499 1
500
    /**
501
     * Submits the Issue
502 7
     *
503 1
     * @param string $type The type you want to set the data for.
504 1
     *  - Needs to be in the allowedTypes already.
505
     * @param array<string, mixed> $data The array of details about the issue.
506
     * @throws \Fr3nch13\Jira\Exception\IssueSubmissionException If submitting the issue fails.
507 6
     * @throws \Fr3nch13\Jira\Exception\MissingAllowedTypeException If that issue type is not configured.
508
     * @throws \Fr3nch13\Jira\Exception\MissingIssueFieldException If we're adding a new issue, and required fields aren't defined.
509 6
     * @return int > 0 If the request was successfully submitted.
510
     */
511
    public function submitIssue(string $type, array $data = []): int
512 6
    {
513
        if (!$this->isAllowedType($type)) {
514
            $this->setJiraError($type, 'MissingAllowedTypeException');
515
            throw new MissingAllowedTypeException($type);
516
        }
517
518
        if (!isset($data['summary'])) {
519
            $this->setJiraError('summary', 'MissingIssueFieldException');
520
            throw new MissingIssueFieldException('summary');
521
        }
522
523
        $issueField = $this->buildSubmittedIssue($type, $data);
524
525
        $issueService = new IssueService($this->ConfigObj);
526
527
        try {
528
            $ret = $issueService->create($issueField);
529
        } catch (JiraException $e) {
530
            //Sample return error with json in it.
531
            //Pasting here so I can mock this return message in the unit tests.
532
            //CURL HTTP Request Failed: Status Code : 400, URL:https://[hostname]/rest/api/2/issue
533
            //Error Message : {"errorMessages":[],"errors":{"user_type":"Field 'user_type' cannot be set. It is not on the appropriate screen, or unknown."}}             */
534
            $msg = $e->getMessage();
535
            if ($msg) {
536
                $msg = $this->extractJiraError($msg);
537
            }
538
539
            $this->setJiraError($msg, 'IssueSubmissionException');
540
            throw new IssueSubmissionException($msg);
541
        }
542
543
        if ($ret instanceof Issue && $ret->id) {
544
            return (int)$ret->id;
545
        }
546
547
        return 0;
548
    }
549
550
    /**
551
     * Creates the issue to send to the server.
552
     *
553
     * @param string $type The type of isse we're creating.
554
     * @param array<string, mixed> $data The data from the submitted form.
555 6
     * @throws \Fr3nch13\Jira\Exception\MissingProjectException If submitting the issue fails.
556 6
     * @return \JiraRestApi\Issue\IssueField
557
     */
558
    public function buildSubmittedIssue(string $type, array $data = []): IssueField
559
    {
560
        $typeInfo = $this->getAllowedTypes($type);
561
562
        // make sure we can get the project info first.
563
        // getInfo will throw an exception if it can't find the project.
564
        // putting a try/catch around it so scrutinizer stops complaining.
565
        try {
566
            $project = $this->getInfo();
567
        } catch (MissingProjectException $e) {
568
            $this->setJiraError($this->projectKey, 'MissingProjectException');
569
            throw $e;
570 7
        }
571
572 7
        $issueField = new IssueField();
573
        $issueField->setProjectKey($this->projectKey)
574
            ->setIssueType($typeInfo['jiraType']);
575
576
        if (isset($data['summary'])) {
577
            $issueField->setSummary($data['summary']);
578 7
        }
579
        if (isset($data['description'])) {
580
            $issueField->setDescription($data['description']);
581
        }
582
        if (isset($data['priority'])) {
583
            $issueField->setPriorityName($data['priority']);
584 7
        }
585 7
        if (isset($data['assignee'])) {
586 7
            $issueField->setAssigneeName($data['assignee']);
587
        }
588 7
        if (isset($data['version'])) {
589 7
            $issueField->addVersion($data['version']);
590
        }
591 7
        if (isset($data['components'])) {
592 2
            $issueField->addComponents($data['components']);
593
        }
594 7
        if (isset($data['duedate'])) {
595 1
            $issueField->setDueDate($data['duedate']);
596
        }
597 7
598 1
        // labels should be space seperated
599
        if (isset($typeInfo['jiraLabels'])) {
600 7
            if (is_string($typeInfo['jiraLabels'])) {
601 1
                $typeInfo['jiraLabels'] = preg_split('/\s+/', $typeInfo['jiraLabels']);
602
            }
603 7
            // track the type with a label
604 1
            $typeInfo['jiraLabels'][] = 'user-submitted-type-' . $type;
605
            foreach ($typeInfo['jiraLabels'] as $jiralabel) {
606 7
                $issueField->addLabel($jiralabel);
607 1
            }
608
        }
609
610
        return $issueField;
611 7
    }
612 7
613 7
    /**
614
     * Sets an error
615
     *
616 7
     * @param string $msg The error message.
617 7
     * @param string $key The key to use in the this->errors array.
618 7
     * @return bool If saved or not.
619
     */
620
    public function setJiraError(string $msg = '', string $key = ''): bool
621
    {
622 7
        if (!trim($msg)) {
623
            return false;
624
        }
625
        if ($key) {
626
            $this->errors[$key] = $msg;
627
        } else {
628
            $this->errors[] = $msg;
629
        }
630
631
        return true;
632 21
    }
633
634 21
    /**
635 1
     * Gets the accumulated error messages.
636
     * If a key is given, return that specific message. If that key doesn't exist, return false.
637 21
     *
638 21
     * @return array<int|string, string>
639
     */
640 1
    public function getJiraErrors(): array
641
    {
642
        return $this->errors;
643 21
    }
644
645
    /**
646
     * Extracts the error message from the JiraException.
647
     *
648
     * @param string $msg The message from the Jir a Exception
649
     * @return string The extracted message if it is in json.
650
     */
651
    public function extractJiraError(string $msg): string
652 1
    {
653
        if (strpos($msg, '{') !== false) {
654 1
            $msgArray = str_split($msg);
655
            // extract the json message.
656
            $json = '';
657
            $in = 0;
658
            foreach ($msgArray as $i => $char) {
659
                if ($char == '{') {
660
                    $in++;
661
                }
662
                if ($in) {
663
                    $json .= $msg[$i];
664
                }
665
                if ($char == '}') {
666
                    $in--;
667
                }
668
            }
669
            if ($json) {
670
                $json = json_decode($json, true);
671
            }
672
            if ($json) {
673
                $newMsg = [];
674
                if (isset($json['errorMessages'])) {
675
                    foreach ($json['errorMessages'] as $jsonMsg) {
676
                        $newMsg[] = $jsonMsg;
677
                    }
678
                    foreach ($json['errors'] as $jsonMsg) {
679
                        $newMsg[] = $jsonMsg;
680
                    }
681
                    $msg = implode("\n", $newMsg);
682
                }
683
            }
684
        }
685
686
        return $msg;
687
    }
688
}
689