Completed
Push — master ( 7ddc12...943a6c )
by Vitaly
02:41
created

CloudReporter::report()   D

Complexity

Conditions 9
Paths 45

Size

Total Lines 35
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 4.909
c 0
b 0
f 0
cc 9
eloc 14
nc 45
nop 0
1
<?php declare(strict_types = 1);
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 22.09.16 at 09:26
5
 */
6
namespace samsonframework\bitbucket;
7
8
use Bitbucket\API\Authentication\AuthenticationInterface;
9
use Bitbucket\API\Repositories\Changesets;
10
use Bitbucket\API\Repositories\PullRequests;
11
use Buzz\Message\MessageInterface;
12
use Psr\Log\LoggerAwareInterface;
13
use Symfony\Component\Console\Logger\ConsoleLogger;
14
15
/**
16
 * Class BitBucketCloudReporter.
17
 *
18
 * @author Vitaly Egorov <[email protected]>
19
 */
20
class CloudReporter
21
{
22
    /** @var PullRequests */
23
    protected $pullRequests;
24
25
    /** @var Changesets */
26
    protected $changeSets;
27
28
    /** @var string BitBucket account name */
29
    protected $accountName;
30
31
    /** @var string BitBucket repository name */
32
    protected $repoName;
33
34
    /** @var int BitBucket pull request id */
35
    protected $pullRequestId;
36
37
    /** @var ConsoleLogger */
38
    protected $logger;
39
40
    /** @var string Pull request author */
41
    protected $author;
42
43
    /** @var ReporterInterface[] */
44
    protected $reporters = [];
45
46
    public function __construct(
47
        AuthenticationInterface $credentials,
48
        ConsoleLogger $logger,
49
        string $accountName,
50
        string $repoName,
51
        int $pullRequestId
52
    ) {
53
        $this->accountName = trim($accountName);
54
        $this->repoName = trim($repoName);
55
        $this->pullRequestId = $pullRequestId;
56
        $this->logger = $logger;
57
58
        $this->pullRequests = new PullRequests();
59
        $this->pullRequests->setCredentials($credentials);
60
61
        $this->changesets = new Changesets();
0 ignored issues
show
Bug introduced by
The property changesets does not seem to exist. Did you mean changeSets?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
62
        $this->changesets->setCredentials(clone $credentials);
0 ignored issues
show
Bug introduced by
The property changesets does not seem to exist. Did you mean changeSets?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
63
64
        $this->author = $this->getPullRequestAuthor();
65
    }
66
67
    /**
68
     * Add reporter.
69
     *
70
     * @param ReporterInterface $reporter Reporter instance
71
     */
72
    public function addReporter(ReporterInterface $reporter)
73
    {
74
        $this->reporters[] = $reporter;
75
    }
76
77
    /**
78
     * Report violations to BitBucket pull request.
79
     */
80
    public function report()
81
    {
82
        // Gather all violations
83
        $violations = [];
84
        foreach ($this->reporters as $reporter) {
85
            if ($reporter instanceof ViolationReporterInterface) {
86
                /** @var ViolationReporterInterface $reporter */
87
                $violations = array_merge($violations, $reporter->parseViolations());
88
            }
89
        }
90
91
        $this->logger->log(ConsoleLogger::INFO, 'Found '.count($violations).' files with violations');
92
93
        // Iterate only files changed by pull request
94
        foreach ($this->getChangedFiles() as $file) {
95
            // Check if we have PMD violations in that files
96
            if (array_key_exists($file, $violations)) {
97
                // Iterate file violations
98
                // TODO: Check lines if they are within this changeset
99
                foreach ($violations[$file] as $line => $violations) {
100
                    // Iterate file line violations
101
                    foreach ($violations as $violation) {
102
                        // Send comment to BitBucket pull request
103
                        $this->createFileComment($violation, $file, $line);
104
                    }
105
                }
106
            }
107
        }
108
109
        if (array_key_exists(ScreenshotReporter::MARKER, $violations)) {
110
            foreach ($violations[ScreenshotReporter::MARKER][0] as $screenshot) {
111
                $this->createGeneralComment('![Screent shot]('.$screenshot.')');
112
            }
113
        }
114
    }
115
116
    /**
117
     * Get pull request author username.
118
     *
119
     * @return string Pull request author username
120
     */
121
    public function getPullRequestAuthor()
122
    {
123
        $responseString = $this->pullRequests->get($this->accountName, $this->repoName, $this->pullRequestId);
124
        try {
125
            $responseObject = json_decode($responseString->getContent());
126
127
            if (isset($responseObject->error)) {
128
                $this->logger->critical($responseObject->error->message);
129
            } elseif (isset($responseObject->author)) {
130
                return $responseObject->author->username;
131
            } else {
132
                $this->logger->log(ConsoleLogger::INFO, 'BitBucket response has no values');
133
            }
134
        } catch (\InvalidArgumentException $exception) {
135
            $this->logger->critical('Cannot json_decode BitBucket response');
136
        }
137
    }
138
139
    /**
140
     * Collection of changed files in pull request.
141
     *
142
     * @return string[] Collection of changed files
143
     */
144
    public function getChangedFiles()
145
    {
146
        $files = [];
147
        $responseString = $this->pullRequests->commits($this->accountName, $this->repoName, $this->pullRequestId);
148
149
        try {
150
            $responseObject = json_decode($responseString->getContent());
151
152
            if (isset($responseObject->error)) {
153
                $this->logger->critical($responseObject->error->message);
154
            } elseif (isset($responseObject->values) && is_array($responseObject->values)) {
155
                foreach ($responseObject->values as $commit) {
156
                    $changeSet = $this->changesets->diffstat($this->accountName, $this->repoName, $commit->hash);
0 ignored issues
show
Bug introduced by
The property changesets does not seem to exist. Did you mean changeSets?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
157
                    $files[] = json_decode($changeSet->getContent())[0]->file;
158
                }
159
            } else {
160
                $this->logger->log(ConsoleLogger::INFO, 'BitBucket response has no values');
161
            }
162
        } catch (\InvalidArgumentException $exception) {
163
            $this->logger->critical('Cannot json_decode BitBucket response');
164
        }
165
166
        return $files;
167
    }
168
169
    /**
170
     * Create general pull request comment.
171
     *
172
     * @param string $content The comment content
173
     *
174
     * @return MessageInterface
175
     */
176
    public function createGeneralComment(string $content)
177
    {
178
        return $this->postComment(['content' => $content]);
179
    }
180
181
    /**
182
     * Add a new comment to pull request.
183
     *
184
     * @param string      $content  The comment content
185
     * @param null|string $filename File name
186
     * @param int|null    $lineFrom Source code line number
187
     *
188
     * @return MessageInterface
189
     */
190
    public function createFileComment(string $content, string $filename = null, int $lineFrom = null) : MessageInterface
191
    {
192
        $this->logger->log(ConsoleLogger::INFO, 'Creating comment in: '.$filename.'#'.$lineFrom.' - '.$content);
193
194
        return $this->postComment([
195
            'content' => $content,
196
            'filename' => $filename,
197
            'line_from' => $lineFrom
198
        ]);
199
    }
200
201
    /**
202
     * Low level post request for creating pull request comment.
203
     *
204
     * @param array $commentData Comment data
205
     *
206
     * @return MessageInterface
207
     */
208
    protected function postComment(array $commentData)
209
    {
210
        // Add pull request author
211
        $commentData['content'] = '@'.$this->author.' '.$commentData['content'];
212
213
        // Switch to old API version
214
        $this->pullRequests->getClient()->setApiVersion('1.0');
215
216
        return $this->pullRequests->comments()->requestPost(
217
            sprintf('repositories/%s/%s/pullrequests/%d/comments', $this->accountName, $this->repoName, $this->pullRequestId),
218
            $commentData
219
        );
220
    }
221
}
222