Passed
Push — master ( a0c2c0...1d066e )
by Ronan
05:56 queued 02:14
created

ProjectController::add()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 17
c 2
b 0
f 0
dl 0
loc 27
ccs 0
cts 26
cp 0
rs 9.7
cc 3
nc 3
nop 2
crap 12
1
<?php
2
3
namespace App\Controller;
4
5
use App\Controller\Traits\ProjectTrait;
6
use App\Facades\Log;
7
use App\Facades\Provider;
8
use App\Facades\Router;
9
use App\Facades\Security;
10
use App\Facades\Session;
11
use App\Facades\View;
12
use App\Model\Deployment;
13
use App\Model\Event;
14
use App\Model\Project;
15
use App\Queue\DeployJob;
16
use App\Queue\ReactivateJob;
17
use Exception;
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use Ronanchilvers\Foundation\Facade\Queue;
21
use Ronanchilvers\Foundation\Queue\Exception\FailedDispatchException;
22
use Ronanchilvers\Orm\Orm;
23
use Ronanchilvers\Utility\Str;
24
use RuntimeException;
25
26
/**
27
 * Controller for the index
28
 *
29
 * @author Ronan Chilvers <[email protected]>
30
 */
31
class ProjectController
32
{
33
    use ProjectTrait;
34
35
    /**
36
     * Index action
37
     *
38
     * @author Ronan Chilvers <[email protected]>
39
     */
40
    public function index(
41
        ServerRequestInterface $request,
42
        ResponseInterface $response
43
    ) {
44
        $user = Security::user();
45
        $all = Orm::finder(Project::class)->all();
46
47
        $userFavourites = $user->preference('favourites', []);
48
        $favourites = $projects = [];
49
        foreach ($all as $project) {
50
            if (in_array($project->id, $userFavourites)) {
51
                $favourites[] = $project;
52
                continue;
53
            }
54
            $projects[] = $project;
55
        }
56
57
        return View::render(
58
            $response,
59
            'project/index.html.twig',
60
            [
61
                'favourites' => $favourites,
62
                'projects'   => $projects
63
            ]
64
        );
65
    }
66
67
    /**
68
     * View a project dashboard
69
     *
70
     * @author Ronan Chilvers <[email protected]>
71
     */
72
    public function view(
73
        ServerRequestInterface $request,
74
        ResponseInterface $response,
75
        $args
76
    ) {
77
        if (!$project = $this->projectFromArgs($args)) {
78
            return $response->withRedirect(
0 ignored issues
show
Bug introduced by
The method withRedirect() does not exist on Psr\Http\Message\ResponseInterface. It seems like you code against a sub-type of Psr\Http\Message\ResponseInterface such as Slim\Http\Response. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

78
            return $response->/** @scrutinizer ignore-call */ withRedirect(
Loading history...
79
                Router::pathFor('project.index')
80
            );
81
        }
82
83
        $finder = Orm::finder(Deployment::class);
84
        $deployments = $finder->forProject($project);
85
86
        $selected_number = $request->getQueryParam(
0 ignored issues
show
Bug introduced by
The method getQueryParam() does not exist on Psr\Http\Message\ServerRequestInterface. Did you maybe mean getQueryParams()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

86
        /** @scrutinizer ignore-call */ 
87
        $selected_number = $request->getQueryParam(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
87
            'deployment',
88
            (0 < count($deployments)) ? $deployments[0]->number : false
89
        );
90
        $selectedDeployment = (0 < count($deployments)) ? $deployments[0] : false;
91
        foreach ($deployments as $deployment) {
92
            if ($deployment->number == $selected_number) {
93
                $selectedDeployment = $deployment;
94
                break;
95
            }
96
        }
97
        $events = [];
98
        if ($selectedDeployment) {
99
            $events = Orm::finder(Event::class)->arrayForDeploymentId(
100
                $selectedDeployment->id
101
            );
102
            // $events = $selectedDeployment->events;
103
        }
104
105
        return View::render(
106
            $response,
107
            'project/view.html.twig',
108
            [
109
                'project'             => $project,
110
                'deployments'         => $deployments,
111
                'selected_deployment' => $selectedDeployment,
112
                'events'              => $events,
113
            ]
114
        );
115
    }
116
117
    /**
118
     * Add a project
119
     *
120
     * @author Ronan Chilvers <[email protected]>
121
     */
122
    public function add(
123
        ServerRequestInterface $request,
124
        ResponseInterface $response
125
    ) {
126
        $project = new Project;
127
        if ($request->isMethod('POST')) {
0 ignored issues
show
Bug introduced by
The method isMethod() does not exist on Psr\Http\Message\ServerRequestInterface. It seems like you code against a sub-type of Psr\Http\Message\ServerRequestInterface such as Slim\Http\Request. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
        if ($request->/** @scrutinizer ignore-call */ isMethod('POST')) {
Loading history...
128
            $data = $request->getParsedBody()['project'];
129
            $project->fromArray($data);
130
            if ($project->saveWithValidation()) {
131
                Session::flash([
132
                    'heading' => 'Project added'
133
                ], 'success');
134
                return $response->withRedirect(
135
                    Router::pathFor('project.view', ['key' => $project->key])
136
                );
137
            }
138
            Log::debug('Project add failed', [
139
                'errors' => $project->getErrors()
140
            ]);
141
        }
142
143
        return View::render(
144
            $response,
145
            'project/add.html.twig',
146
            [
147
                'project' => $project,
148
                'providers' => Provider::getOptions(),
149
            ]
150
        );
151
    }
152
153
    /**
154
     * Edit action
155
     *
156
     * @author Ronan Chilvers <[email protected]>
157
     */
158
    public function edit(
159
        ServerRequestInterface $request,
160
        ResponseInterface $response,
161
        $args
162
    ) {
163
        if (!$project = $this->projectFromArgs($args)) {
164
            return $response->withRedirect(
165
                Router::pathFor('project.index')
166
            );
167
        }
168
        if ($request->isMethod('POST')) {
169
            $data = $request->getParsedBody()['project'];
170
            $project->fromArray($data);
171
            if ($project->saveWithValidation()) {
172
                Session::flash([
173
                    'heading' => 'Project saved'
174
                ]);
175
                return $response->withRedirect(
176
                    Router::pathFor('project.edit', [
177
                        'key' => $project->key
178
                    ])
179
                );
180
            }
181
        }
182
183
        return View::render(
184
            $response,
185
            'project/edit.html.twig',
186
            [
187
                'project' => $project,
188
                'providers' => Provider::getOptions(),
189
            ]
190
        );
191
    }
192
193
    /**
194
     * Prepare a deployment
195
     *
196
     * @author Ronan Chilvers <[email protected]>
197
     */
198
    public function prepareDeploy(
199
        ServerRequestInterface $request,
200
        ResponseInterface $response,
201
        $args
202
    ) {
203
        if (!$project = $this->projectFromArgs($args)) {
204
            return $response->withRedirect(
205
                Router::pathFor('project.index')
206
            );
207
        }
208
209
        return View::render(
210
            $response,
211
            'project/prepare-deploy.html.twig',
212
            [
213
                'project' => $project,
214
            ]
215
        );
216
    }
217
218
    /**
219
     * Trigger a deploy for a project
220
     *
221
     * @author Ronan Chilvers <[email protected]>
222
     */
223
    public function deploy(
224
        ServerRequestInterface $request,
225
        ResponseInterface $response,
226
        $args
227
    ) {
228
        try {
229
            if (!$project = $this->projectFromArgs($args)) {
230
                return $response->withRedirect(
231
                    Router::pathFor('project.index')
232
                );
233
            }
234
            $input  = $request->getParsedBodyParam('project', []);
0 ignored issues
show
Bug introduced by
The method getParsedBodyParam() does not exist on Psr\Http\Message\ServerRequestInterface. Did you maybe mean getParsedBody()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

234
            /** @scrutinizer ignore-call */ 
235
            $input  = $request->getParsedBodyParam('project', []);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
235
            $branch = (!isset($input['branch']) || empty($input['branch'])) ? 'master' : $input['branch'];
236
            $provider = Provider::forProject(
237
                $project
238
            );
239
            $finder = Orm::finder(Event::class);
240
            Orm::transaction(function() use ($project, $provider, $branch, $response, $finder) {
241
                try {
242
                    $deployment = Orm::finder(Deployment::class)->nextForProject(
243
                        $project
244
                    );
245
                    $deployment->source = Security::email();
246
                    if (!$deployment->save()) {
247
                        Log::debug('Unable to create new deployment object', [
248
                            'project' => $project->toArray(),
249
                        ]);
250
                        throw new RuntimeException('Unable to create new deployment');
251
                    }
252
                    $finder->event(
253
                        'info',
254
                        $deployment,
255
                        'Initialise',
256
                        sprintf("Querying %s for head commit data", $provider->getLabel())
257
                    );
258
                    $head = $provider->getHeadInfo($project->repository, $branch);
259
                    $finder->event(
260
                        'info',
261
                        $deployment,
262
                        'Initialise',
263
                        "Commit data : " . json_encode($head, JSON_PRETTY_PRINT)
264
                    );
265
                    Log::debug('Updating deployment commit information', $head);
266
                    $deployment->sha       = $head['sha'];
267
                    $deployment->author    = $head['author'];
268
                    $deployment->committer = $head['committer'];
269
                    $deployment->message   = $head['message'];
270
                    if (!$deployment->save()) {
271
                        return $response->withRedirect(
272
                            Router::pathFor('project.view', [
273
                                'key' => $project->key
274
                            ])
275
                        );
276
                    }
277
                    if (!$project->markDeploying()) {
278
                        throw new RuntimeException('Unable to mark project as deploying');
279
                    }
280
                    Queue::dispatch(
281
                        new DeployJob($deployment)
282
                    );
283
                } catch (Exception $ex) {
284
                    if (isset($deployment) && $deployment instanceof Deployment) {
285
                        $finder->event(
286
                            'error',
287
                            $deployment,
288
                            'Initialise',
289
                            $ex->getMessage()
290
                        );
291
                    }
292
                    throw $ex;
293
                }
294
            });
295
296
            Session::flash([
297
                'heading' => 'Deploy queued successfully'
298
            ]);
299
        } catch (Exception $ex) {
300
            $message = [$ex->getMessage()];
301
            if ($previous = $ex->getPrevious()) {
302
                $message[] = $previous->getMessage();
303
            }
304
            $message = implode(' - ', $message);
305
            Session::flash(
306
                [
307
                    'heading' => 'Failed to initialise new deployment',
308
                    'content' => $message,
309
                ],
310
                'error'
311
            );
312
            Log::error('Failed to initialise new deployment', [
313
                'exception' => $ex,
314
            ]);
315
        }
316
317
        return $response->withRedirect(
318
            Router::pathFor('project.view', [
319
                'key' => $project->key
320
            ])
321
        );
322
    }
323
324
    /**
325
     * Trigger a deploy for a project
326
     *
327
     * @author Ronan Chilvers <[email protected]>
328
     */
329
    public function redeploy(
330
        ServerRequestInterface $request,
331
        ResponseInterface $response,
332
        $args
333
    ) {
334
        try {
335
            if (!$project = $this->projectFromArgs($args)) {
336
                return $response->withRedirect(
337
                    Router::pathFor('project.index')
338
                );
339
            }
340
            Orm::transaction(function() use ($project, $args) {
341
                $dummy = Orm::finder(Deployment::class)->nextForProject(
342
                    $project
343
                );
344
                $original = Orm::finder(Deployment::class)->one(
345
                    $args['deployment']
346
                );
347
                if (!$original instanceof Deployment) {
348
                    throw new RuntimeException('Invalid attempt to re-deploy non-existant deployment');
349
                }
350
                $deployment           = clone $original;
351
                $deployment->original = $original;
352
                $deployment->number   = $dummy->number;
353
                if (!$deployment->save()) {
354
                    Log::debug('Unable to create deployment object', [
355
                        'project' => $project->toArray(),
356
                    ]);
357
                    throw new RuntimeException('Unable to create new deployment');
358
                }
359
                if (!$project->markDeploying()) {
360
                    throw new RuntimeException('Unable to mark project as deploying');
361
                }
362
                Queue::dispatch(
363
                    new ReactivateJob($original, $deployment)
364
                );
365
            });
366
            Session::flash([
367
                'heading' => 'Re-deploy queued successfully'
368
            ]);
369
        } catch (Exception $ex) {
370
            Session::flash(
371
                [
372
                    'heading' => 'Failed to initialise re-deployment',
373
                    'content' => $ex->getMessage(),
374
                ],
375
                'error'
376
            );
377
            Log::error('Failed to initialise re-deployment', [
378
                'exception' => $ex,
379
            ]);
380
        }
381
382
        return $response->withRedirect(
383
            Router::pathFor('project.view', [
384
                'key' => $project->key
385
            ])
386
        );
387
    }
388
}
389