Completed
Push — master ( eadaa3...ee6db2 )
by Thomas
07:49
created

GenerateApiCommand::generateOperation()   D

Complexity

Conditions 15
Paths 62

Size

Total Lines 105
Code Lines 72

Duplication

Lines 12
Ratio 11.43 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 7
Bugs 0 Features 0
Metric Value
c 7
b 0
f 0
dl 12
loc 105
ccs 0
cts 70
cp 0
rs 4.9121
cc 15
eloc 72
nc 62
nop 2
crap 240

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
namespace keeko\tools\command;
3
4
use gossi\swagger\collections\Paths;
5
use gossi\swagger\Swagger;
6
use keeko\tools\command\AbstractGenerateCommand;
7
use keeko\tools\utils\NameUtils;
8
use phootwork\file\File;
9
use phootwork\json\Json;
10
use Propel\Generator\Model\Table;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Output\OutputInterface;
13 20
use gossi\swagger\collections\Definitions;
14 20
15 20
class GenerateApiCommand extends AbstractGenerateCommand {
16 20
17
	protected function configure() {
18
		$this
19 20
			->setName('generate:api')
20 20
			->setDescription('Generates the api for the module')
21
		;
22
		
23
		$this->configureGenerateOptions();
24
			
25
		parent::configure();
26
	}
27
	
28
	/**
29
	 * Checks whether api can be generated at all by reading composer.json and verify
30
	 * all required information are available
31
	 */
32
	private function preCheck() {
33
		$module = $this->packageService->getModule();
34
		if ($module === null) {
35
			throw new \DomainException('No module definition found in composer.json - please run `keeko init`.');
36
		}
37
	}
38
39
	protected function execute(InputInterface $input, OutputInterface $output) {
40
		$api = new File($this->project->getApiFileName());
41
		if (!$this->project->hasApiFile()) {
42
			$api->write('{}');
43
		}
44
45
		$swagger = Swagger::fromFile($this->project->getApiFileName());
46
		$swagger->setVersion('2.0');
47
		$this->generatePaths($swagger);
48
		$this->generateDefinitions($swagger);
49
		
50
		$api->write(Json::encode($swagger->toArray(), Json::PRETTY_PRINT | Json::UNESCAPED_SLASHES));
51
	}
52
	
53
	protected function generatePaths(Swagger $swagger) {
54
		$paths = $swagger->getPaths();
55
		
56
		foreach ($this->packageService->getModule()->getActionNames() as $name) {
57
			$this->generateOperation($paths, $name);
58
		}
59
	}
60
	
61
	protected function generateOperation(Paths $paths, $actionName) {
62
		$this->logger->notice('Generating Operation for: ' . $actionName);
63
	
64
		$database = $this->modelService->getDatabase();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
65
		$action = $this->packageService->getAction($actionName);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
66
		$modelName = $this->modelService->getModelNameByAction($action);
0 ignored issues
show
Bug introduced by
It seems like $action defined by $this->packageService->getAction($actionName) on line 65 can be null; however, keeko\tools\services\Mod...:getModelNameByAction() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
67
		$tableName = $this->modelService->getTableName($modelName);
68
	
69
		if (!$database->hasTable($tableName)) {
70
			return $paths;
71
		}
72
	
73
		$type = $this->packageService->getActionType($actionName, $modelName);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 12 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
74
		$modelObjectName = $database->getTable($tableName)->getPhpName();
75
		$modelPluralName = NameUtils::pluralize($modelName);
76
	
77
		// find path branch
78
		switch ($type) {
79
			case 'list':
80
			case 'create':
81
				$endpoint = '/' . $modelPluralName;
82
				break;
83
	
84
			case 'read':
85
			case 'update':
86
			case 'delete':
87
				$endpoint = '/' . $modelPluralName . '/{id}';
88
				break;
89
	
90
			default:
91
				throw new \RuntimeException('type (%s) not found, can\'t continue.');
92
				break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
93
		}
94
	
95
	
96
		$path = $paths->get($endpoint);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
97
		$method = $this->getMethod($type);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
98
		$operation = $path->getOperation($method);
99
		$operation->setDescription($action->getTitle());
100
		$operation->setOperationId($action->getName());
101
		$operation->getProduces()->add('application/json');
102
	
103
		$params = $operation->getParameters();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
104
		$responses = $operation->getResponses();
105
	
106
		switch ($type) {
107
			case 'list':
108
				$ok = $responses->get('200');
109
				$ok->setDescription(sprintf('Array of %s', $modelPluralName));
110
				$ok->getSchema()->setRef('#/definitions/' . 'Paged' . NameUtils::pluralize($modelObjectName));
111
				break;
112
	
113
			case 'create':
114
				// params
115
				$body = $params->getByName('body');
116
				$body->setName('body');
117
				$body->setIn('body');
118
				$body->setDescription(sprintf('The new %s', $modelName));
119
				$body->setRequired(true);
120
				$body->getSchema()->setRef('#/definitions/Writable' . $modelObjectName);
121
	
122
				// response
123
				$ok = $responses->get('201');
124
				$ok->setDescription(sprintf('%s created', $modelName));
125
				break;
126
	
127 View Code Duplication
			case 'read':
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...
128
				// response
129
				$ok = $responses->get('200');
130
				$ok->setDescription(sprintf('gets the %s', $modelName));
131
				$ok->getSchema()->setRef('#/definitions/' . $modelObjectName);
132
				break;
133
	
134 View Code Duplication
			case 'update':
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...
135
				// response
136
				$ok = $responses->get('200');
137
				$ok->setDescription(sprintf('%s updated', $modelName));
138
				$ok->getSchema()->setRef('#/definitions/' . $modelObjectName);
139
				break;
140
	
141
			case 'delete':
142
				// response
143
				$ok = $responses->get('204');
144
				$ok->setDescription(sprintf('%s deleted', $modelName));
145
				break;
146
		}
147
	
148
		if ($type == 'read' || $type == 'update' || $type == 'delete') {
149
			// params
150
			$id = $params->getByName('id');
151
			$id->setIn('path');
152
			$id->setDescription(sprintf('The %s id', $modelName));
153
			$id->setRequired(true);
154
			$id->setType('integer');
155
	
156
			// response
157
			$invalid = $responses->get('400');
158
			$invalid->setDescription('Invalid ID supplied');
159
				
160
			$notfound = $responses->get('404');
161
			$notfound->setDescription(sprintf('No %s found', $modelName));
162
		}
163
	
164
		// response - @TODO Error model
165
	}
166
	
167
	private function getMethod($type) {
168
		$methods = [
169
			'list' => 'GET',
170
			'create' => 'POST',
171
			'read' => 'GET',
172
			'update' => 'PUT',
173
			'delete' => 'DELETE'
174
		];
175
	
176
		return $methods[$type];
177
	}
178
179
	protected function generateDefinitions(Swagger $swagger) {
180
		$definitions = $swagger->getDefinitions();
181
		
182
		// meta
183
		$this->generateMeta($definitions);
184
185
		// models
186
		$modelName = $this->modelService->getModelName();
187
		if ($modelName !== null) {
188
			$definitions = $this->generateDefinition($definitions, $modelName);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $definitions is correct as $this->generateDefinitio...efinitions, $modelName) (which targets keeko\tools\command\Gene...d::generateDefinition()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
$definitions is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
189
		} else {
190
			foreach ($this->modelService->getModels() as $model) {
191
				$definitions = $this->generateDefinition($definitions, $model->getName());
0 ignored issues
show
Bug introduced by
It seems like $definitions defined by $this->generateDefinitio...ons, $model->getName()) on line 191 can be null; however, keeko\tools\command\Gene...d::generateDefinition() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
Are you sure the assignment to $definitions is correct as $this->generateDefinitio...ons, $model->getName()) (which targets keeko\tools\command\Gene...d::generateDefinition()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
192
			}
193
		}
194
	}
195
	
196
	protected function generateMeta(Definitions $definitions) {
197
		$meta = $definitions->get('Meta');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
198
		$props = $meta->getProperties();
199
		$names = ['total', 'first', 'next', 'previous', 'last'];
200
		
201
		foreach ($names as $name) {
202
			$props->get($name)->setType('integer');
203
		}
204
	}
205
206
	protected function generateDefinition(Definitions $definitions, $modelName) {
207
		$this->logger->notice('Generating Definition for: ' . $modelName);
208
		$model = $this->modelService->getModel($modelName);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
209
		$modelObjectName = $model->getPhpName();
210
		$modelPluralName = NameUtils::pluralize($model->getOriginCommonName());
211
		
212
		// paged model
213
		$pagedModel = 'Paged' . NameUtils::pluralize($modelObjectName);
214
		$paged = $definitions->get($pagedModel)->getProperties();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
215
		$paged->get($modelPluralName)
216
			->setType('array')
217
			->getItems()->setRef('#/definitions/' . $modelObjectName);
218
		$paged->get('meta')->setRef('#/definitions/Meta');
219
		
220
		// writable model
221
		$writable = $definitions->get('Writable' . $modelObjectName)->getProperties();
222
		$this->generateModelProperties($writable, $model, true);
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 208 can be null; however, keeko\tools\command\Gene...nerateModelProperties() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
223
224
		// readable model
225
		$readable = $definitions->get($modelObjectName)->getProperties();
226
		$this->generateModelProperties($readable, $model, true);
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 208 can be null; however, keeko\tools\command\Gene...nerateModelProperties() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
227
	}
228
	
229
	protected function generateModelProperties(Definitions $props, Table $model, $write = false) {
230
		$modelName = $model->getOriginCommonName();
231
		$filter = $write 
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
232
			? $this->codegenService->getCodegen()->getWriteFilter($modelName)
233
			: $this->codegenService->getCodegen()->getReadFilter($modelName);
234
		
235
		if ($write) {
236
			$filter = array_merge($filter, $this->codegenService->getComputedFields($model));
237
		}
238
		foreach ($model->getColumns() as $col) {
239
			$prop = $col->getName();
240
			
241
			if (!in_array($prop, $filter)) {
242
				$props->get($prop)->setType($col->getPhpType());
243
			}
244
		}
245
246
		return $props;
247
	}
248
249
}