Skip to content

Commit 8ab90bf

Browse files
authored
Input params and handlers
* Split InputParam to multiple subclasses * Added attributes to parameters and handlers
1 parent bf28492 commit 8ab90bf

31 files changed

+814
-230
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
88

99
* Update nette libs to version 3.0.0 (BC break)
1010
* Added typehints (BC break)
11+
* Splitted InputParam to multiple subclasses (BC break)
12+
* Removed type TYPE_POST_JSON_KEY (BC break)
13+
* Wrong input now returns code 400 instead of 500 (BC break if somebody checks return code)
1114
* Pretty JSON output in API console - without escaping unicode and slashes
1215
* Replaced handler information array triplet (endpoint, handler, authorization) with Api
1316

17+
#### Added
18+
19+
* Added type JsonInputParam with scheme as replacement for type TYPE_POST_JSON_KEY
20+
* Detailed error for wrong input if debugger is enabled
21+
* Added summary (short description), description, tags and deprecated flag for API handlers
22+
* Added description, default value and example for input params
23+
1424
#### Removed
1525

1626
* Removed support for PHP 5.6, 7.0 and hhvm (BC Break)

README.md

+11-11
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ services:
5555
- Tomaj\NetteApi\Misc\IpDetector
5656
apiDecider:
5757
factory: Tomaj\NetteApi\ApiDecider
58-
setup:
59-
- addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\NoAuthorization())
60-
- addApi(\Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'users', 'send-email'), \App\MyApi\v1\Handlers\SendEmailHandler(), \Tomaj\NetteApi\Authorization\BearerTokenAuthorization())
58+
setup:
59+
- addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\NoAuthorization())
60+
- addApi(\Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'users', 'send-email'), \App\MyApi\v1\Handlers\SendEmailHandler(), \Tomaj\NetteApi\Authorization\BearerTokenAuthorization())
6161
```
6262

6363
As you can see in example, you can register as many endpoints as you want with different configurations. Nette-Api supports API versioning from the beginning.
@@ -179,8 +179,8 @@ Example with user detail:
179179
namespace App\MyApi\v1\Handlers;
180180

181181
use Tomaj\NetteApi\Handlers\BaseHandler;
182+
use Tomaj\NetteApi\Params\GetInputParam;
182183
use Tomaj\NetteApi\Response\JsonApiResponse;
183-
use Tomaj\NetteApi\Params\InputParam;
184184
use Tomaj\NetteApi\Response\ResponseInterface;
185185

186186
class UsersDetailHandler extends Basehandler
@@ -196,7 +196,7 @@ class UsersDetailHandler extends Basehandler
196196
public function params(): array
197197
{
198198
return [
199-
new InputParam(InputParam::TYPE_GET, 'id', InputParam::REQUIRED),
199+
(new GetInputParam('id'))->setRequired(),
200200
];
201201
}
202202

@@ -224,12 +224,12 @@ This is table with support input types:
224224

225225
| Input type | Example
226226
| ---------- | -------
227-
| POST | `new InputParam(InputParam::TYPE_POST, 'key')`
228-
| GET | `new InputParam(InputParam::TYPE_GET, 'key')`
229-
| FILE | `new InputParam(InputParam::TYPE_FILE, 'key')`
230-
| COOKIE | `new InputParam(InputParam::TYPE_COOKIE, 'key')`
231-
| RAW POST | `new InputParam(InputParam::TYPE_POST_RAW, 'key')`
232-
| JSON | `new InputParam(InputParam::TYPE_POST_JSON_KEY, 'key')`
227+
| POST | `new PostInputParam('key')`
228+
| GET | `new GetInputParam('key')`
229+
| FILE | `new FileInputParam('key')`
230+
| COOKIE | `new CookieInputParam('key')`
231+
| RAW POST | `new RawInputParam('key')`
232+
| JSON | `new JsonInputParam('key', '{"type": "object"}')`
233233

234234

235235
## Security

UPGRADE.md

+39-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@
22

33
## Upgrade from 1.x to 2.0.0
44

5+
### Splitted InputParam to multiple subclasses
6+
InputParam is now abstract class and all types have their own class. Also InputParam is more like Nette Form inputs with fluent API
7+
8+
Examples of replacements:
9+
10+
Requred get input with available values:
11+
12+
Old:
13+
```php
14+
new InputParam(InputParam::TYPE_GET, 'status', InputParam::REQUIRED, ['ok', 'error'])
15+
```
16+
17+
New:
18+
```php
19+
(new GetInputParam('status'))->setRequired()->setAvailableValues(['ok', 'error'])
20+
```
21+
22+
Multiple optional file input:
23+
24+
Old:
25+
```php
26+
new InputParam(InputParam::TYPE_FILE, 'myfile', InputParam::OPTIONAL, null, true)
27+
```
28+
29+
New:
30+
```php
31+
(new FileInputParam('myfile'))->setMulti()
32+
```
33+
534
### Removed support for old PHP versions
635
New version not supported PHP versions 5.6 and 7.0 and also hhvm. Please use it with newer versions of PHP (>7.1)
736

@@ -47,6 +76,9 @@ Add typehints to methods:
4776
- `getApiAction(): ?string`
4877
- `getUrl(): string`
4978

79+
### Changed behavior
80+
API handler tripplet (array of endpoint, handler, authorization) has been changed to class `Api` which has methods `getEndpoint()`, `getHandler()` and `getAuthorization()`.
81+
5082
### Renamed methods
5183
Few methods have been renamed, please use their new versions:
5284
- `ApiDecider::addApiHandler()` -> `ApiDecider::addApi()`
@@ -62,7 +94,7 @@ BaseHandler now have few final methods:
6294

6395
### Removed params
6496
Parameters $parent and $name have been removed from ApiListingControl. New usage is:
65-
```
97+
```php
6698
new ApiListingControl($apiDecider)
6799
```
68100

@@ -75,8 +107,13 @@ Some parameters were strictly typed:
75107
### Changed events
76108
Registration of event onClick in ApiListingControl.
77109
Use:
78-
```
110+
```php
79111
$apiListing->onClick[] = function ($method, $version, $package, $apiAction) {
80112
...
81113
};
82114
```
115+
116+
### Features
117+
With new version of Nette API you can:
118+
- add description for your API handlers, also you can mark some handlers as deprecated and add tags for them.
119+
- add description, default value and example for all your input params

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"nette/http": "^3.0",
2121
"tracy/tracy": "^2.6",
2222
"league/fractal": "^0.17.0",
23-
"tomaj/nette-bootstrap-form": "^2.0"
23+
"tomaj/nette-bootstrap-form": "^2.0",
24+
"justinrainbow/json-schema": "^5.2"
2425
},
2526
"require-dev": {
2627
"nette/di": "^3.0",

src/Component/ApiConsoleControl.php

+4-53
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use Tomaj\NetteApi\EndpointInterface;
1414
use Tomaj\NetteApi\Handlers\ApiHandlerInterface;
1515
use Tomaj\NetteApi\Misc\ConsoleRequest;
16-
use Tomaj\NetteApi\Params\InputParam;
1716

1817
class ApiConsoleControl extends Control
1918
{
@@ -36,6 +35,7 @@ public function __construct(IRequest $request, EndpointInterface $endpoint, ApiH
3635
public function render(): void
3736
{
3837
$this->getTemplate()->setFile(__DIR__ . '/console.latte');
38+
$this->getTemplate()->add('handler', $this->handler);
3939
$this->getTemplate()->render();
4040
}
4141

@@ -66,7 +66,7 @@ protected function createComponentConsoleForm(): Form
6666

6767
if ($this->authorization instanceof BearerTokenAuthorization) {
6868
$form->addText('token', 'Token')
69-
->setAttribute('placeholder', 'Enter token');
69+
->setHtmlAttribute('placeholder', 'Enter token');
7070
} elseif ($this->authorization instanceof NoAuthorization) {
7171
$form->addText('authorization', 'Authorization')
7272
->setDisabled(true);
@@ -76,45 +76,11 @@ protected function createComponentConsoleForm(): Form
7676
$form->addCheckbox('send_session_id', 'Send session id cookie');
7777

7878
$params = $this->handler->params();
79-
$jsonField = null;
80-
$jsonParams = [];
8179
foreach ($params as $param) {
82-
$count = $param->isMulti() ? 5 : 1;
83-
for ($i = 0; $i < $count; $i++) {
84-
$key = $param->getKey();
85-
if ($param->isMulti()) {
86-
$key = $key . '___' . $i;
87-
}
88-
89-
if ($param->getAvailableValues() && is_array($param->getAvailableValues())) {
90-
$c = $form->addSelect($key, $this->getParamLabel($param), array_combine($param->getAvailableValues(), $param->getAvailableValues()));
91-
if (!$param->isRequired()) {
92-
$c->setPrompt('Select ' . $this->getLabel($param));
93-
}
94-
} elseif ($param->getAvailableValues() && is_string($param->getAvailableValues())) {
95-
$c = $form->addText($key, $this->getParamLabel($param))->setDisabled(true);
96-
$defaults[$key] = $param->getAvailableValues();
97-
} elseif ($param->getType() == InputParam::TYPE_FILE) {
98-
$c = $form->addUpload($key, $this->getParamLabel($param));
99-
} elseif ($param->getType() == InputParam::TYPE_POST_RAW) {
100-
$c = $form->addTextArea('post_raw', $this->getParamLabel($param))
101-
->setAttribute('rows', 10);
102-
} elseif ($param->getType() == InputParam::TYPE_POST_JSON_KEY) {
103-
if ($jsonField === null) {
104-
$jsonField = $form->addTextArea('post_raw', 'JSON')
105-
->setOption('description', 'Empty string means "key is required", null means "key is optional"');
106-
}
107-
$jsonParams[$key] = $param->isRequired() ? '' : null;
108-
} else {
109-
$c = $form->addText($key, $this->getParamLabel($param));
110-
}
111-
}
112-
}
113-
if ($jsonField !== null && $jsonParams) {
114-
$jsonField->setDefaultValue(json_encode($jsonParams, JSON_PRETTY_PRINT));
80+
$param->updateConsoleForm($form);
11581
}
11682

117-
$form->addSubmit('send', 'Otestuj')
83+
$form->addSubmit('send', 'Try api')
11884
->getControlPrototype()
11985
->setName('button')
12086
->setHtml('<i class="fa fa-cloud-upload"></i> Try api');
@@ -125,21 +91,6 @@ protected function createComponentConsoleForm(): Form
12591
return $form;
12692
}
12793

128-
private function getLabel(InputParam $param): string
129-
{
130-
return ucfirst(str_replace('_', ' ', $param->getKey()));
131-
}
132-
133-
private function getParamLabel(InputParam $param): string
134-
{
135-
$title = $this->getLabel($param);
136-
if ($param->isRequired()) {
137-
$title .= ' *';
138-
}
139-
$title .= ' (' . $param->getType() . ')';
140-
return $title;
141-
}
142-
14394
public function formSucceeded(Form $form, ArrayHash $values): void
14495
{
14596
$url = $values['api_url'];

src/Component/api_listing.latte

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<div class="api-listing">
22
<div n:foreach="$apis as $version => $versionApis" class="listing-verions listing-version-{$version}">
3+
{php $showSummary = false}
4+
{foreach $versionApis as $api}
5+
{if $api->getHandler()->summary()}
6+
{php $showSummary = true}
7+
{breakIf true}
8+
{/if}
9+
{/foreach}
10+
311
<h3>Version {$version}</h3>
412

513
<div class="table-responsive">
@@ -9,16 +17,20 @@
917
<th>Method</th>
1018
<th>Url</th>
1119
<th>Handler</th>
20+
<th n:if="$showSummary">Summary</th>
1221
<th>Authorization</th>
1322
</tr>
1423
</thead>
1524
<tbody>
1625
<tr n:foreach="$versionApis as $api">
1726
<td><span class="label label-default">{$api->getEndpoint()->getMethod()}</span></td>
1827
<td>
19-
<a href="{link Select! $api->getEndpoint()->getMethod(), $api->getEndpoint()->getVersion(), $api->getEndpoint()->getPackage(), $api->getEndpoint()->getApiAction()}"><b>/{$api->getEndpoint()->getUrl()}</b></a>
28+
<s n:tag-if="$api->getHandler()->deprecated()" title="Deprecated">
29+
<a href="{link Select! $api->getEndpoint()->getMethod(), $api->getEndpoint()->getVersion(), $api->getEndpoint()->getPackage(), $api->getEndpoint()->getApiAction()}"><b>/{$api->getEndpoint()->getUrl()}</b></a>
30+
</s>
2031
</td>
2132
<td>{get_class($api->getHandler())}</td>
33+
<td n:if="$showSummary">{$api->getHandler()->summary()}</td>
2234
<td>{get_class($api->getAuthorization())}</td>
2335
</tr>
2436
</tbody>

src/Component/console.latte

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
21
<h2>Api Web Console</h2>
32

3+
<div n:if="$handler->description() || $handler->deprecated() || $handler->tags()">
4+
<span n:if="$handler->deprecated()" class="btn btn-sm btn-danger">API is deprecated</span>
5+
<span n:foreach="$handler->tags() as $tag" class="btn btn-sm btn-primary">{$tag}</span>
6+
<p n:if="$handler->description()">{$handler->description()}</p>
7+
</div>
8+
49
{ifset $response}
510
<div class="row">
611
<div class="col-md-12">

src/Handlers/ApiHandlerInterface.php

+26-2
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,42 @@
33
namespace Tomaj\NetteApi\Handlers;
44

55
use Tomaj\NetteApi\EndpointInterface;
6-
use Tomaj\NetteApi\Params\InputParam;
6+
use Tomaj\NetteApi\Params\ParamInterface;
77
use Tomaj\NetteApi\Response\ResponseInterface;
88

99
interface ApiHandlerInterface
1010
{
11+
/**
12+
* Summary of handler - short description of handler
13+
* @return string
14+
*/
15+
public function summary(): string;
16+
17+
/**
18+
* Description of handler
19+
* @return string
20+
*/
21+
public function description(): string;
22+
1123
/**
1224
* Returns available parameters that handler need
1325
*
14-
* @return InputParam[]
26+
* @return ParamInterface[]
1527
*/
1628
public function params(): array;
1729

30+
/**
31+
* Returns list of tags for handler
32+
* @return array
33+
*/
34+
public function tags(): array;
35+
36+
/**
37+
* Marks handler as deprecated
38+
* @return bool
39+
*/
40+
public function deprecated(): bool;
41+
1842
/**
1943
* Main handle method that will be executed when api
2044
* endpoint contected with this handler will be triggered

src/Handlers/BaseHandler.php

+33-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ public function __construct(ScopeFactoryInterface $scopeFactory = null)
3232
$this->fractal = new Manager($scopeFactory);
3333
}
3434

35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function summary(): string
39+
{
40+
return '';
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function description(): string
47+
{
48+
return '';
49+
}
50+
3551
/**
3652
* {@inheritdoc}
3753
*/
@@ -40,10 +56,26 @@ public function params(): array
4056
return [];
4157
}
4258

59+
/**
60+
* {@inheritdoc}
61+
*/
62+
public function tags(): array
63+
{
64+
return [];
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public function deprecated(): bool
71+
{
72+
return false;
73+
}
74+
4375
protected function getFractal(): Manager
4476
{
4577
if (!$this->fractal) {
46-
throw new InvalidStateException("Fractal manager isnt initialized. Did you call parent::__construct() in your handler constructor?");
78+
throw new InvalidStateException("Fractal manager isn't initialized. Did you call parent::__construct() in your handler constructor?");
4779
}
4880
return $this->fractal;
4981
}

0 commit comments

Comments
 (0)