JiraProject::getJiraErrors()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 66
    public function __construct()
161
    {
162 66
        $this->configure();
163
164
        // setup the objects
165 66
        $this->ProjectService = new ProjectService($this->ConfigObj);
166
        try {
167 66
            $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
            $msg = __('{0}: {1}', [
174
                $this->projectKey,
175
                $msg,
176
            ]);
177
            $this->setJiraError($msg, 'MissingProjectException');
178
            throw new MissingProjectException($msg);
179
        }
180
181 66
        $this->Versions = (array)$this->ProjectService->getVersions($this->projectKey);
182 66
        $this->IssueService = new IssueService($this->ConfigObj);
183
    }
184
185
    /**
186
     * Configures the object.
187
     * Broken out of construct.
188
     *
189
     * @throws \Fr3nch13\Jira\Exception\MissingConfigException When a config setting isn't set.
190
     * @return void
191
     */
192 66
    public function configure(): void
193
    {
194 66
        $schema = Configure::read('Jira.schema');
195 66
        if (!$schema) {
196 1
            $this->setJiraError('schema', 'MissingConfigException');
197 1
            throw new MissingConfigException('schema');
198
        }
199 66
        $host = Configure::read('Jira.host');
200 66
        if (!$host) {
201 1
            $this->setJiraError('host', 'MissingConfigException');
202 1
            throw new MissingConfigException('host');
203
        }
204 66
        $username = Configure::read('Jira.username');
205 66
        if (!$username) {
206 1
            $this->setJiraError('username', 'MissingConfigException');
207 1
            throw new MissingConfigException('username');
208
        }
209 66
        $apiKey = Configure::read('Jira.apiKey');
210 66
        if (!$apiKey) {
211 1
            $this->setJiraError('apiKey', 'MissingConfigException');
212 1
            throw new MissingConfigException('apiKey');
213
        }
214 66
        $projectKey = Configure::read('Jira.projectKey');
215 66
        if (!$projectKey) {
216 1
            $this->setJiraError('projectKey', 'MissingConfigException');
217 1
            throw new MissingConfigException('projectKey');
218
        }
219 66
        $useV3RestApi = Configure::read('Jira.useV3RestApi');
220 66
        if (!$useV3RestApi) {
221 1
            $this->setJiraError('useV3RestApi', 'MissingConfigException');
222 1
            throw new MissingConfigException('useV3RestApi');
223
        }
224 66
        $jiraLogFile = Configure::read('Jira.jiraLogFile');
225 66
        if (!$jiraLogFile) {
226 1
            $this->setJiraError('jiraLogFile', 'MissingConfigException');
227 1
            throw new MissingConfigException('jiraLogFile');
228
        }
229 66
        $this->ConfigObj = new ArrayConfiguration([
230 66
            'jiraHost' => $schema . '://' . $host,
231 66
            'jiraUser' => $username,
232 66
            'jiraPassword' => $apiKey,
233 66
            'useV3RestApi' => $useV3RestApi,
234 66
            'jiraLogFile' => $jiraLogFile,
235 66
        ]);
236
237 66
        $this->projectKey = $projectKey;
238
    }
239
240
    /**
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 9
    public function getInfo(): \JiraRestApi\Project\Project
247
    {
248 9
        return $this->Project;
249
    }
250
251
    /**
252
     * Get the Project's Versions.
253
     *
254
     * @return array<\JiraRestApi\Issue\Version> A list of version objects.
255
     */
256 2
    public function getVersions(): array
257
    {
258 2
        return $this->Versions;
259
    }
260
261
    /**
262
     * Get the Project's Issues.
263
     *
264
     * @param string|null $type Filter the Issues by type.
265
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
266
     */
267 4
    public function getIssues(?string $type = null): \JiraRestApi\Issue\IssueSearchResult
268
    {
269 4
        $cacheKey = 'all';
270 4
        if ($type) {
271 2
            $cacheKey .= '-' . $type;
272
        }
273 4
        if (!isset($this->Issues[$cacheKey])) {
274 4
            $jql = new JqlQuery();
275
276 4
            $jql->setProject($this->projectKey);
277 4
            if ($type && in_array($type, $this->validTypes)) {
278 2
                $jql->setType($type);
279
            }
280 4
            $jql->addAnyExpression('ORDER BY key DESC');
281
282 4
            $this->Issues[$cacheKey] = $this->IssueService->search($jql->getQuery(), 0, 1000);
283
        }
284
285 4
        return $this->Issues[$cacheKey];
286
    }
287
288
    /**
289
     * Get the Project's Open Issues.
290
     *
291
     * @param string|null $type Filter the Issues by type.
292
     * @return \JiraRestApi\Issue\IssueSearchResult|\JiraRestApi\Issue\IssueSearchResultV3 A list of issue objects.
293
     */
294 4
    public function getOpenIssues(?string $type = null): \JiraRestApi\Issue\IssueSearchResult
295
    {
296 4
        $cacheKey = 'open';
297 4
        if ($type) {
298 2
            $cacheKey .= '-' . $type;
299
        }
300 4
        if (!isset($this->Issues[$cacheKey])) {
301 4
            $jql = new JqlQuery();
302
303 4
            $jql->setProject($this->projectKey);
304 4
            if ($type && in_array($type, $this->validTypes)) {
305 2
                $jql->setType($type);
306
            }
307 4
            $jql->addAnyExpression('AND resolution is EMPTY');
308 4
            $jql->addAnyExpression('ORDER BY key DESC');
309
310 4
            $this->Issues[$cacheKey] = $this->IssueService->search($jql->getQuery(), 0, 1000);
311
        }
312
313 4
        return $this->Issues[$cacheKey];
314
    }
315
316
    /**
317
     * Gets info on a particular issue within your project.
318
     *
319
     * @param int|null $id The issue id. The integer part without the project key.
320
     * @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
     * @return \JiraRestApi\Issue\Issue|\JiraRestApi\Issue\IssueV3 the object that has the info of that issue.
323
     */
324 4
    public function getIssue(?int $id = null): Issue
325
    {
326 4
        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 3
        $key = $this->projectKey . '-' . $id;
331 3
        if (!isset($this->issuesCache[$key])) {
332
            try {
333 3
                $this->issuesCache[$key] = $this->IssueService->get($key);
334 1
            } catch (JiraException $e) {
335 1
                $msg = $e->getMessage();
336 1
                if ($msg) {
337 1
                    $msg = $this->extractJiraError($msg);
338
                }
339 1
                $msg = __('{0}: {1}', [
340 1
                    $key,
341 1
                    $msg,
342 1
                ]);
343 1
                $this->setJiraError($msg, 'MissingIssueException');
344 1
                throw new MissingIssueException($msg);
345
            }
346
        }
347
348 2
        return $this->issuesCache[$key];
349
    }
350
351
    /**
352
     * 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 2
    public function getBugs(): \JiraRestApi\Issue\IssueSearchResult
357
    {
358 2
        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 2
    public function getOpenBugs(): \JiraRestApi\Issue\IssueSearchResult
367
    {
368 2
        return $this->getOpenIssues('Bug');
369
    }
370
371
    /**
372
     * Methods used to submit an Issue to Jira.
373
     */
374
375
    /**
376
     * Returns the allowed types and their settings
377
     *
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 29
    public function getAllowedTypes(?string $type = null): array
383
    {
384 29
        if ($type) {
385 8
            if (!isset($this->allowedTypes[$type])) {
386 1
                $this->setJiraError($type, 'MissingAllowedTypeException');
387 1
                throw new MissingAllowedTypeException($type);
388
            }
389
390 7
            return $this->allowedTypes[$type];
391
        }
392
393 26
        return $this->allowedTypes;
394
    }
395
396
    /**
397
     * Allows you to modify the form allowdTypes to fir your situation.
398
     *
399
     * @param string $type The type of issue you want to add/modify.
400
     * @param array<string, mixed> $settings The settings for the type.
401
     * @throws \Fr3nch13\Jira\Exception\MissingIssueFieldException If we're adding a new issue type, and the summary field isn't defined.
402
     * @return void
403
     */
404 16
    public function modifyAllowedTypes(string $type, array $settings = []): void
405
    {
406 16
        if (!isset($this->allowedTypes[$type])) {
407 16
            $this->allowedTypes[$type] = [];
408 16
            if (!isset($settings['jiraType'])) {
409 1
                $this->setJiraError('jiraType', 'MissingIssueFieldException');
410 1
                throw new MissingIssueFieldException('jiraType');
411
            }
412 15
            if (!isset($settings['formData'])) {
413 1
                $this->setJiraError('formData', 'MissingIssueFieldException');
414 1
                throw new MissingIssueFieldException('formData');
415
            }
416 14
            if (!isset($settings['formData']['fields'])) {
417 1
                $this->setJiraError('formData.fields', 'MissingIssueFieldException');
418 1
                throw new MissingIssueFieldException('formData.fields');
419
            }
420 13
            if (!isset($settings['formData']['fields']['summary'])) {
421 1
                $this->setJiraError('formData.fields.summary', 'MissingIssueFieldException');
422 1
                throw new MissingIssueFieldException('formData.fields.summary');
423
            }
424
        }
425
426 12
        $this->allowedTypes[$type] += $settings;
427
    }
428
429
    /**
430
     * Checks to see if a type is allowed.
431
     *
432
     * @param string $type The type to check.
433
     * @return bool if it's allowed or not.
434
     */
435 32
    public function isAllowedType(string $type): bool
436
    {
437 32
        return isset($this->allowedTypes[$type]) ? true : false;
438
    }
439
440
    /**
441
     * 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
     * @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
     * @return array<string, mixed> The array of data to fill in the form with.
447
     */
448 27
    public function getFormData(?string $type = null): array
449
    {
450 27
        if (!$type) {
451 1
            $this->setJiraError('[$type is not set]', 'MissingAllowedTypeException');
452 1
            throw new MissingAllowedTypeException('[$type is not set]');
453
        }
454
455 26
        if (!$this->isAllowedType($type)) {
456 1
            $this->setJiraError($type, 'MissingAllowedTypeException');
457 1
            throw new MissingAllowedTypeException($type);
458
        }
459
460 25
        $allowedTypes = $this->getAllowedTypes();
461
462 25
        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 25
        if (!isset($allowedTypes[$type]['formData']['fields'])) {
468
            $this->setJiraError('No form data fields are set.', 'MissingDataException');
469
            throw new MissingDataException(__('No form data fields are set.'));
470
        }
471
472 25
        return $allowedTypes[$type]['formData'];
473
    }
474
475
    /**
476
     * Sets the formData variable if you want to modify the default/initial values.
477
     *
478
     * @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
     * @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 26
    public function setFormData(string $type, array $data = []): void
486
    {
487 26
        if (!$this->isAllowedType($type)) {
488 1
            $this->setJiraError($type, 'MissingAllowedTypeException');
489 1
            throw new MissingAllowedTypeException($type);
490
        }
491
492 25
        if (!isset($data['fields'])) {
493 1
            $this->setJiraError('No form data fields are set.', 'MissingDataException');
494 1
            throw new MissingDataException(__('No form data fields are set.'));
495
        }
496
497 24
        $this->allowedTypes[$type]['formData'] = $data;
498
    }
499
500
    /**
501
     * Submits the Issue
502
     *
503
     * @param string $type The type you want to set the data for.
504
     *  - 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
     * @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
     * @return int > 0 If the request was successfully submitted.
510
     */
511 8
    public function submitIssue(string $type, array $data = []): int
512
    {
513 8
        if (!$this->isAllowedType($type)) {
514 1
            $this->setJiraError($type, 'MissingAllowedTypeException');
515 1
            throw new MissingAllowedTypeException($type);
516
        }
517
518 7
        if (!isset($data['summary'])) {
519 1
            $this->setJiraError('summary', 'MissingIssueFieldException');
520 1
            throw new MissingIssueFieldException('summary');
521
        }
522
523 6
        $issueField = $this->buildSubmittedIssue($type, $data);
524
525 6
        $issueService = new IssueService($this->ConfigObj);
526
527
        try {
528 6
            $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 6
        if ($ret instanceof Issue && $ret->id) {
544 6
            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
     * @throws \Fr3nch13\Jira\Exception\MissingProjectException If submitting the issue fails.
556
     * @return \JiraRestApi\Issue\IssueField
557
     */
558 7
    public function buildSubmittedIssue(string $type, array $data = []): IssueField
559
    {
560 7
        $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 7
            $project = $this->getInfo();
567
        } catch (MissingProjectException $e) {
568
            $this->setJiraError($this->projectKey, 'MissingProjectException');
569
            throw $e;
570
        }
571
572 7
        $issueField = new IssueField();
573 7
        $issueField->setProjectKey($this->projectKey)
574 7
            ->setIssueType($typeInfo['jiraType']);
575
576 7
        if (isset($data['summary'])) {
577 7
            $issueField->setSummary($data['summary']);
578
        }
579 7
        if (isset($data['description'])) {
580 2
            $issueField->setDescription($data['description']);
581
        }
582 7
        if (isset($data['priority'])) {
583 1
            $issueField->setPriorityName($data['priority']);
584
        }
585 7
        if (isset($data['assignee'])) {
586 1
            $issueField->setAssigneeName($data['assignee']);
587
        }
588 7
        if (isset($data['version'])) {
589 1
            $issueField->addVersion($data['version']);
590
        }
591 7
        if (isset($data['components'])) {
592 1
            $issueField->addComponents($data['components']);
593
        }
594 7
        if (isset($data['duedate'])) {
595 1
            $issueField->setDueDate($data['duedate']);
596
        }
597
598
        // labels should be space seperated
599 7
        if (isset($typeInfo['jiraLabels'])) {
600 7
            if (is_string($typeInfo['jiraLabels'])) {
601 7
                $typeInfo['jiraLabels'] = preg_split('/\s+/', $typeInfo['jiraLabels']);
602
            }
603
            // track the type with a label
604 7
            $typeInfo['jiraLabels'][] = 'user-submitted-type-' . $type;
605 7
            foreach ($typeInfo['jiraLabels'] as $jiralabel) {
606 7
                $issueField->addLabel($jiralabel);
607
            }
608
        }
609
610 7
        return $issueField;
611
    }
612
613
    /**
614
     * Sets an error
615
     *
616
     * @param string $msg The error message.
617
     * @param string $key The key to use in the this->errors array.
618
     * @return bool If saved or not.
619
     */
620 21
    public function setJiraError(string $msg = '', string $key = ''): bool
621
    {
622 21
        if (!trim($msg)) {
623 1
            return false;
624
        }
625 21
        if ($key) {
626 21
            $this->errors[$key] = $msg;
627
        } else {
628 1
            $this->errors[] = $msg;
629
        }
630
631 21
        return true;
632
    }
633
634
    /**
635
     * Gets the accumulated error messages.
636
     * If a key is given, return that specific message. If that key doesn't exist, return false.
637
     *
638
     * @return array<int|string, string>
639
     */
640 1
    public function getJiraErrors(): array
641
    {
642 1
        return $this->errors;
643
    }
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 2
    public function extractJiraError(string $msg): string
652
    {
653 2
        if (strpos($msg, '{') !== false) {
654 1
            $msgArray = str_split($msg);
655
            // extract the json message.
656 1
            $json = '';
657 1
            $in = 0;
658 1
            foreach ($msgArray as $i => $char) {
659 1
                if ($char == '{') {
660 1
                    $in++;
661
                }
662 1
                if ($in) {
663 1
                    $json .= $msg[$i];
664
                }
665 1
                if ($char == '}') {
666 1
                    $in--;
667
                }
668
            }
669 1
            if ($json) {
670 1
                $json = json_decode($json, true);
671
            }
672 1
            if ($json) {
673 1
                $newMsg = [];
674 1
                if (isset($json['errorMessages'])) {
675 1
                    foreach ($json['errorMessages'] as $jsonMsg) {
676
                        $newMsg[] = $jsonMsg;
677
                    }
678 1
                    foreach ($json['errors'] as $jsonMsg) {
679 1
                        $newMsg[] = $jsonMsg;
680
                    }
681 1
                    $msg = implode("\n", $newMsg);
682
                }
683
            }
684
        }
685
686 2
        return $msg;
687
    }
688
}
689