Completed
Pull Request — master (#730)
by Sean
04:20
created

GitDispatcher::show()   F

Complexity

Conditions 18
Paths 768

Size

Total Lines 109
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 109
rs 2.3577
c 0
b 0
f 0
cc 18
eloc 77
nc 768
nop 1

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
3
/**
4
 * This dispatcher takes care of updating and returning information about this
5
 * projects git repository
6
 */
7
class GitDispatcher extends Dispatcher {
0 ignored issues
show
Coding Style introduced by
The property $allowed_actions is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $action_types is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8
9
	const ACTION_GIT = 'git';
10
11
	const REF_TYPE_FROM_UAT = 0;
12
	const REF_TYPE_BRANCH = 1;
13
	const REF_TYPE_TAG = 2;
14
	const REF_TYPE_PREVIOUS = 3;
15
	const REF_TYPE_SHA = 4;
16
17
	/**
18
	 * @var array
19
	 */
20
	public static $allowed_actions = [
21
		'update',
22
		'show'
23
	];
24
25
	/**
26
	 * @var \DNProject
27
	 */
28
	protected $project = null;
29
30
	/**
31
	 * @var \DNEnvironment
32
	 */
33
	protected $environment = null;
34
35
	/**
36
	 * @var array
37
	 */
38
	private static $action_types = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $action_types is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
39
		self::ACTION_GIT
40
	];
41
42
	public function init() {
43
		parent::init();
44
45
		$this->project = $this->getCurrentProject();
46
47
		if (!$this->project) {
48
			return $this->project404Response();
49
		}
50
	}
51
52
	/**
53
	 *
54
	 * @param \SS_HTTPRequest $request
55
	 *
56
	 * @return \HTMLText|\SS_HTTPResponse
57
	 */
58
	public function index(\SS_HTTPRequest $request) {
59
		return $this->redirect(\Controller::join_links($this->Link(), 'show'), 302);
60
	}
61
62
	/**
63
	 * @param SS_HTTPRequest $request
64
	 * @return SS_HTTPResponse
65
	 */
66
	public function update(\SS_HTTPRequest $request) {
67
		switch ($request->httpMethod()) {
68
			case 'POST':
69
				$this->checkSecurityToken();
70
				return $this->createUpdate();
71
			case 'GET':
72
				return $this->getUpdateStatus($this->getRequest()->param('ID'));
73
			default:
74
				return $this->getAPIResponse(['message' => 'Method not allowed, requires POST or GET/{id}'], 405);
75
		}
76
	}
77
78
	/**
79
	 * @param SS_HTTPRequest $request
80
	 *
81
	 * @return string
82
	 */
83
	public function show(\SS_HTTPRequest $request) {
84
		$targetEnvironment = null;
85
		$targetEnvironmentId = $request->getVar('environmentId');
86
		if (!empty($targetEnvironmentId)) {
87
			$targetEnvironment = DNEnvironment::get()->byId((int) $targetEnvironmentId);
88
		}
89
90
		$refs = [];
91
		$prevDeploys = [];
92
93
		$uatEnvironment = $this->project->DNEnvironmentList()->filter('Usage', DNEnvironment::UAT)->first();
94
		$uatBuild = $uatEnvironment ? $uatEnvironment->CurrentBuild() : null;
95
		if ($uatBuild && $uatBuild->exists() && $targetEnvironment && $targetEnvironment->Usage === DNEnvironment::PRODUCTION) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
96
			$refs[self::REF_TYPE_FROM_UAT] = [
97
				'id' => self::REF_TYPE_FROM_UAT,
98
				'label' => 'Promote the version currently on UAT',
99
				'description' => 'Promote the version currently on UAT',
100
				'promote_build' => [
101
					'id' => $uatBuild->ID,
102
					'deployed' => DBField::create_field('SS_Datetime', $uatBuild->Created, 'Created')->Ago(),
103
					'branch' => $uatBuild->Branch,
104
					'tags' => $uatBuild->getTags()->toArray(),
105
					'sha' => $uatBuild->SHA,
106
					'short_sha' => substr($uatBuild->SHA, 0, 7),
107
					'commit_message' => $uatBuild->getCommitMessage(),
108
					'commit_url' => $uatBuild->getCommitURL(),
109
				]
110
			];
111
		}
112
113
		$refs[self::REF_TYPE_BRANCH] = [
114
			'id' => self::REF_TYPE_BRANCH,
115
			'label' => 'Branch version',
116
			'description' => 'Deploy the latest version of a branch',
117
			'list' => $this->getGitBranches($this->project)
118
		];
119
120
		$refs[self::REF_TYPE_TAG] = [
121
			'id' => self::REF_TYPE_TAG,
122
			'label' => 'Tag version',
123
			'description' => 'Deploy a tagged release',
124
			'list' => $this->getGitTags($this->project)
125
		];
126
127
		// @todo: the original was a tree that was keyed by environment, the
128
		// front-end dropdown needs to be changed to support that. brrrr.
129
		foreach ($this->getGitPrevDeploys($this->project) as $env) {
130
			foreach ($env as $deploy) {
131
				$prevDeploys[] = $deploy;
132
			}
133
		}
134
		$refs[self::REF_TYPE_PREVIOUS] = [
135
			'id' => self::REF_TYPE_PREVIOUS,
136
			'label' => 'Redeploy a release that was previously deployed (to any environment)',
137
			'description' => 'Deploy a previous release',
138
			'list' => $prevDeploys
139
		];
140
		$refs[self::REF_TYPE_SHA] = [
141
			'id' => self::REF_TYPE_SHA,
142
			'label' => 'Deploy a specific SHA',
143
			'description' => 'Deploy a specific SHA'
144
		];
145
146
		$options = [];
147
		if ($targetEnvironment) {
148
			foreach ($targetEnvironment->getSupportedOptions() as $option) {
149
				$options[] = [
150
					'name' => $option->getName(),
151
					'title' => $option->getTitle(),
152
					'defaultValue' => $option->getDefaultValue()
153
				];
154
			}
155
		}
156
157
		// get the last time git fetch was run
158
		$lastFetchedDate = 'never';
159
		$lastFetchedAgo = null;
160
		$fetch = DNGitFetch::get()
161
			->filter('ProjectID', $this->project->ID)
162
			->sort('LastEdited', 'DESC')
163
			->first();
164
		if ($fetch) {
165
			$lastFetchedDate = $fetch->obj('LastEdited')->Date();
166
			$lastFetchedAgo = $fetch->obj('LastEdited')->Ago();
167
		}
168
169
		$responseData = [
170
			'refs' => $refs,
171
			'options' => $options,
172
			'last_fetched_date' => $lastFetchedDate,
173
			'last_fetched_ago' => $lastFetchedAgo
174
		];
175
176
		$current = $targetEnvironment ? $targetEnvironment->CurrentBuild() : null;
177
		if ($request->getVar('redeploy') && $request->getVar('redeploy') == '1') {
178
			if ($current && $current->exists()) {
179
				$responseData['preselected_type'] = self::REF_TYPE_PREVIOUS;
180
				$responseData['preselected_sha'] = $current->SHA;
181
			} else {
182
				$master = $this->project->DNBranchList()->byName('master');
183
				if ($master) {
184
					$responseData['preselected_type'] = self::REF_TYPE_BRANCH;
185
					$responseData['preselected_sha'] = $master->SHA();
0 ignored issues
show
Bug introduced by
The method SHA cannot be called on $master (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
186
				}
187
			}
188
		}
189
190
		return $this->getAPIResponse($responseData, 200);
191
	}
192
193
	/**
194
	 * @return string
195
	 */
196
	public function Link() {
197
		return \Controller::join_links($this->project->Link(), self::ACTION_GIT);
198
	}
199
200
	/**
201
	 * @param string $name
202
	 *
203
	 * @return array
204
	 */
205
	public function getModel($name = '') {
206
		return [];
207
	}
208
209
	/**
210
	 * @param int $ID
211
	 * @return SS_HTTPResponse
212
	 */
213
	protected function getUpdateStatus($ID) {
214
		$fetch = DNGitFetch::get()->byID($ID);
215
		if (!$fetch) {
216
			return $this->getAPIResponse(['message' => 'GIT update (' . $ID . ') not found'], 404);
217
		}
218
		$output = [
219
			'id' => $ID,
220
			'status' => $fetch->ResqueStatus(),
221
			'message' => array_filter(explode(PHP_EOL, $fetch->LogContent()))
222
		];
223
224
		return $this->getAPIResponse($output, 200);
225
	}
226
227
	/**
228
	 * @return SS_HTTPResponse
229
	 */
230 View Code Duplication
	protected function createUpdate() {
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...
231
		/** @var DNGitFetch $fetch */
232
		$fetch = DNGitFetch::create();
233
		$fetch->ProjectID = $this->project->ID;
234
		$fetch->write();
235
		$fetch->start();
236
237
		$location = Director::absoluteBaseURL() . $this->Link() . '/update/' . $fetch->ID;
238
		$output = [
239
			'message' => 'git fetch has been queued',
240
			'id' => $fetch->ID,
241
			'location' => $location,
242
		];
243
244
		$response = $this->getAPIResponse($output, 201);
245
		$response->addHeader('Location', $location);
246
		return $response;
247
	}
248
249
	/**
250
	 * @param $project
251
	 *
252
	 * @return array
253
	 */
254
	protected function getGitBranches($project) {
255
		$branches = [];
256
		foreach ($project->DNBranchList() as $branch) {
257
			$branches[] = [
258
				'id' => $branch->SHA(),
259
				'title' => $branch->Name(),
260
			];
261
		}
262
		return $branches;
263
	}
264
265
	/**
266
	 * @param $project
267
	 *
268
	 * @return array
269
	 */
270
	protected function getGitTags($project) {
271
		$tags = [];
272
		foreach ($project->DNTagList()->setLimit(null) as $tag) {
273
			$tags[] = [
274
				'id' => $tag->SHA(),
275
				'title' => $tag->Name(),
276
			];
277
		}
278
		return $tags;
279
	}
280
281
	/**
282
	 * @param $project
283
	 *
284
	 * @return array
285
	 */
286
	protected function getGitPrevDeploys($project) {
287
		$redeploy = [];
288 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...
289
			$envName = $dnEnvironment->Name;
290
			$perEnvDeploys = [];
291
			foreach ($dnEnvironment->DeployHistory() as $deploy) {
292
				$sha = $deploy->SHA;
293
294
				// Check if exists to make sure the newest deployment date is used.
295
				if (!isset($perEnvDeploys[$sha])) {
296
					$pastValue = sprintf(
297
						"%s (deployed %s)",
298
						substr($sha, 0, 8),
299
						$deploy->obj('LastEdited')->Ago()
300
					);
301
					$perEnvDeploys[$sha] = [
302
						'id' => $sha,
303
						'title' => $pastValue
304
					];
305
				}
306
			}
307
			if (!empty($perEnvDeploys)) {
308
				$redeploy[$envName] = array_values($perEnvDeploys);
309
			}
310
		}
311
		return $redeploy;
312
	}
313
314
}
315