Completed
Pull Request — master (#604)
by Stig
03:28
created

DeployPlanDispatcher::gitupdate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
4
class DeployPlanDispatcher extends Dispatcher {
5
6
	const ACTION_PLAN = 'plan';
7
8
	/**
9
	 * @var array
10
	 */
11
	private static $action_types = [
12
		self::ACTION_PLAN
13
	];
14
15
	/**
16
	 * @var array
17
	 */
18
	public static $allowed_actions = [
19
		'gitupdate',
20
		'gitrefs',
21
		'deploysummary'
22
	];
23
24
	/**
25
	 * @var \DNProject
26
	 */
27
	protected $project = null;
28
29
	/**
30
	 * @var \DNEnvironment
31
	 */
32
	protected $environment = null;
33
34
	public function init() {
35
		parent::init();
36
37
		$this->project = $this->getCurrentProject();
38
39
		if(!$this->project) {
40
			return $this->project404Response();
41
		}
42
43
		// Performs canView permission check by limiting visible projects
44
		$this->environment = $this->getCurrentEnvironment($this->project);
45
		if(!$this->environment) {
46
			return $this->environment404Response();
47
		}
48
	}
49
50
	/**
51
	 * @return string
52
	 */
53
	public function Link() {
54
		return \Controller::join_links($this->environment->Link(), self::ACTION_PLAN);
55
	}
56
57
	/**
58
	 *
59
	 * @param \SS_HTTPRequest $request
60
	 *
61
	 * @return \HTMLText|\SS_HTTPResponse
62
	 */
63
	public function index(\SS_HTTPRequest $request) {
64
		$this->setCurrentActionType(self::ACTION_PLAN);
65
		return $this->customise([
66
			'Environment' => $this->environment
67
		])->renderWith(['Plan', 'DNRoot']);
68
	}
69
70
	/**
71
	 * @param SS_HTTPRequest $request
72
	 * @return SS_HTTPResponse
73
	 */
74
	public function gitupdate(SS_HTTPRequest $request) {
75
		switch($request->httpMethod()) {
76
			case 'POST':
77
				return $this->createFetch();
78
			case 'GET':
79
				return $this->getFetch($this->getRequest()->param('ID'));
80
			default:
81
				return $this->getAPIResponse('Method not allowed, requires POST or GET/{id}', 405);
82
		}
83
	}
84
85
	/**
86
	 * @param SS_HTTPRequest $request
87
	 *
88
	 * @return string
89
	 */
90
	public function gitrefs(\SS_HTTPRequest $request) {
91
92
		$refs = [];
93
		$order = 0;
94
		$refs[] = [
95
			'id' => ++$order,
96
			'label' => "Branch version",
97
			"description" => "Deploy the latest version of a branch",
98
			"list" => $this->getGitBranches($this->project)
99
		];
100
101
		$refs[] = [
102
			'id' => ++$order,
103
			'label' => "Tag version",
104
			"description" => "Deploy a tagged release",
105
			"list" => $this->getGitTags($this->project)
106
		];
107
108
		// @todo: the original was a tree that was keyed by environment, the
109
		// front-end dropdown needs to be changed to support that. brrrr.
110
		$prevDeploys = [];
111
		foreach($this->getGitPrevDeploys($this->project) as $env) {
112
			foreach($env as $deploy) {
113
				$prevDeploys[] = $deploy;
114
			}
115
		}
116
		$refs[] = [
117
			'id' => ++$order,
118
			'label' => "Redeploy a release that was previously deployed (to any environment",
119
			"description" => "Deploy a previous release",
120
			"list" => $prevDeploys
121
		];
122
123
		$body = json_encode($refs, JSON_PRETTY_PRINT);
124
		$this->getResponse()->addHeader('Content-Type', 'application/json');
125
		$this->getResponse()->setBody($body);
126
		return $body;
127
	}
128
129
	/**
130
	 * Generate the data structure used by the frontend component.
131
	 *
132
	 * @param string $name of the component
133
	 *
134
	 * @return array
135
	 */
136
	public function getModel($name) {
137
		return [
138
			'APIEndpoint' => Director::absoluteBaseURL().$this->Link()
139
		];
140
	}
141
142
	/**
143
	 * @param int $ID
144
	 * @return SS_HTTPResponse
145
	 */
146
	protected function getFetch($ID) {
147
		$ping = DNGitFetch::get()->byID($ID);
148
		if(!$ping) {
149
			return $this->getAPIResponse('Fetch not found', 404);
150
		}
151
		$output = [
152
			'id' => $ID,
153
			'status' => $ping->ResqueStatus(),
154
			'message' => array_filter(explode(PHP_EOL, $ping->LogContent()))
155
		];
156
157
		return $this->getAPIResponse($output, 200);
0 ignored issues
show
Documentation introduced by
$output is of type array<string,?,{"id":"in..."?","message":"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...
158
	}
159
160
	/**
161
	 * @return SS_HTTPResponse
162
	 */
163 View Code Duplication
	protected function createFetch() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
		/** @var DNGitFetch $fetch */
165
		$fetch = DNGitFetch::create();
166
		$fetch->ProjectID = $this->project->ID;
167
		$fetch->write();
168
		$fetch->start();
169
170
		$location = Director::absoluteBaseURL() . $this->Link() . '/gitupdate/' . $fetch->ID;
171
		$output = array(
172
			'message' => 'Fetch queued as job ' . $fetch->ResqueToken,
173
			'href' => $location,
174
		);
175
176
		$response = $this->getAPIResponse($output, 201);
0 ignored issues
show
Documentation introduced by
$output is of type array<string,string,{"me...ring","href":"string"}>, 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...
177
		$response->addHeader('Location', $location);
178
		return $response;
179
	}
180
181
	/**
182
	 * @param SS_HTTPRequest $request
183
	 *
184
	 * @return SS_HTTPResponse
185
	 */
186
	public function deploysummary(SS_HTTPRequest $request) {
187
188
		// Performs canView permission check by limiting visible projects
189
		$project = $this->getCurrentProject();
190
		if(!$project) {
191
			return $this->project404Response();
192
		}
193
194
		// Performs canView permission check by limiting visible projects
195
		$environment = $this->getCurrentEnvironment($project);
196
		if(!$environment) {
197
			return $this->environment404Response();
198
		}
199
200
		if(!trim($request->getBody())) {
201
			return $this->getAPIResponse(
202
				['message' => 'no body was sent in the request'],
0 ignored issues
show
Documentation introduced by
array('message' => 'no b...s sent in the request') is of type array<string,string,{"message":"string"}>, 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
				400
204
			);
205
		}
206
207
		$jsonBody = json_decode($request->getBody(), true);
208
		if(empty($jsonBody)) {
209
			$output = [
210
				'message' => 'request did not contain a parsable JSON payload' ,
211
			];
212
			return $this->getAPIResponse($output, 400);
0 ignored issues
show
Documentation introduced by
$output is of type array<string,string,{"message":"string"}>, 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...
213
		}
214
215
		$options = [
216
			'sha' => $jsonBody['sha']
217
		];
218
219
		// Plan the deployment.
220
		$strategy = $environment->Backend()->planDeploy($environment, $options);
221
		$data = $strategy->toArray();
222
223
		// Add in a URL for comparing from->to code changes. Ensure that we have
224
		// two proper 40 character SHAs, otherwise we can't show the compare link.
225
		$interface = $project->getRepositoryInterface();
226 View Code Duplication
		if(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
227
			!empty($interface) && !empty($interface->URL)
228
			&& !empty($data['changes']['Code version']['from'])
229
			&& strlen($data['changes']['Code version']['from']) == '40'
230
			&& !empty($data['changes']['Code version']['to'])
231
			&& strlen($data['changes']['Code version']['to']) == '40'
232
		) {
233
			$compareurl = sprintf(
234
				'%s/compare/%s...%s',
235
				$interface->URL,
236
				$data['changes']['Code version']['from'],
237
				$data['changes']['Code version']['to']
238
			);
239
			$data['changes']['Code version']['compareUrl'] = $compareurl;
240
		}
241
242
		// Append json to response
243
		$token = SecurityToken::inst();
244
		$data['SecurityID'] = $token->getValue();
245
246
		$this->extend('updateDeploySummary', $data);
247
248
		return json_encode($data);
249
	}
250
251
	/**
252
	 * @param $project
253
	 *
254
	 * @return array
255
	 */
256
	protected function getGitBranches($project) {
257
		$branches = [];
258
		foreach($project->DNBranchList() as $branch) {
259
			$branches[] = [
260
				'key' => $branch->SHA(),
261
				'value' => $branch->Name(),
262
			];
263
		}
264
		return $branches;
265
	}
266
267
	/**
268
	 * @param $project
269
	 *
270
	 * @return array
271
	 */
272
	protected function getGitTags($project) {
273
		$tags = [];
274
		foreach($project->DNTagList()->setLimit(null) as $tag) {
275
			$tags[] = [
276
				'key' => $tag->SHA(),
277
				'value' => $tag->Name(),
278
			];
279
		}
280
		return $tags;
281
	}
282
283
	/**
284
	 * @param $project
285
	 *
286
	 * @return array
287
	 */
288
	protected function getGitPrevDeploys($project) {
289
		$redeploy = [];
290 View Code Duplication
		foreach($project->DNEnvironmentList() as $dnEnvironment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
			$envName = $dnEnvironment->Name;
292
			$perEnvDeploys = [];
293
			foreach($dnEnvironment->DeployHistory() as $deploy) {
294
				$sha = $deploy->SHA;
295
296
				// Check if exists to make sure the newest deployment date is used.
297
				if(!isset($perEnvDeploys[$sha])) {
298
					$pastValue = sprintf(
299
						"%s (deployed %s)",
300
						substr($sha, 0, 8),
301
						$deploy->obj('LastEdited')->Ago()
302
					);
303
					$perEnvDeploys[$sha] = [
304
						'key' => $sha,
305
						'value' => $pastValue
306
					];
307
				}
308
			}
309
			if(!empty($perEnvDeploys)) {
310
				$redeploy[$envName] = array_values($perEnvDeploys);
311
			}
312
		}
313
		return $redeploy;
314
	}
315
316
	/**
317
	 * Return a simple response with a message
318
	 *
319
	 * @param string $message
320
	 * @param int $statusCode
321
	 * @return SS_HTTPResponse
322
	 */
323
	protected function getAPIResponse($message, $statusCode) {
324
		$output = [
325
			'message' => $message,
326
			'status_code' => $statusCode
327
		];
328
		$body = json_encode($output, JSON_PRETTY_PRINT);
329
		$response = $this->getResponse();
330
		$response->addHeader('Content-Type', 'application/json');
331
		$response->setBody($body);
332
		$response->setStatusCode($statusCode);
333
		return $response;
334
	}
335
}
336