Skip to content

Commit 1b4b5b5

Browse files
authored
Merge pull request #15 from AndreasElia/new/refactor
Refactor Command/Start Tests
2 parents 246305c + 3c2b16f commit 1b4b5b5

File tree

11 files changed

+238
-29
lines changed

11 files changed

+238
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/vendor
22
composer.lock
3+
.phpunit.result.cache

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ You can modify the `api-postman.php` config values:
3232
- `structured` - If you want folders to be generated based on route names.
3333
- `base_url` - The base URL for all of your endpoints.
3434
- `auth_middleware` - The middleware which wraps your authenticated API routes.
35+
- `headers` - The headers applied to all routes within the collection.
3536
- `enable_formdata` - Determines whether or not form data should be handled.
3637
- `formdata` - The key/values to requests for form data dummy information.
3738

composer.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
"AndreasElia\\PostmanGenerator\\": "src/"
2222
}
2323
},
24+
"autoload-dev": {
25+
"psr-4": {
26+
"AndreasElia\\PostmanGenerator\\Tests\\": "tests"
27+
}
28+
},
2429
"require": {
2530
"php": "^7.4|^8.0",
2631
"ext-json": "*",
@@ -38,5 +43,8 @@
3843
}
3944
},
4045
"minimum-stability": "dev",
41-
"prefer-stable": true
46+
"prefer-stable": true,
47+
"require-dev": {
48+
"orchestra/testbench": "^6.12"
49+
}
4250
}

config/api-postman.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@
33
return [
44

55
/*
6-
* Structured.
6+
* Base URL.
77
*
8-
* If you want folders to be generated based on namespace.
8+
* The base URL for all of your endpoints.
99
*/
1010

11-
'structured' => false,
11+
'base_url' => env('APP_URL', 'http://localhost'),
1212

1313
/*
14-
* Base URL.
14+
* Collection filename.
1515
*
16-
* The base URL for all of your endpoints.
16+
* The name for the collection file to be saved.
1717
*/
1818

19-
'base_url' => env('APP_URL', 'http://localhost'),
19+
'filename' => '{timestamp}_{app}_collection.json',
20+
21+
/*
22+
* Structured.
23+
*
24+
* If you want folders to be generated based on namespace.
25+
*/
26+
27+
'structured' => false,
2028

2129
/*
2230
* Auth Middleware.

phpunit.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
bootstrap="vendor/autoload.php"
5+
backupGlobals="false"
6+
backupStaticAttributes="false"
7+
colors="true"
8+
verbose="true"
9+
convertErrorsToExceptions="true"
10+
convertNoticesToExceptions="true"
11+
convertWarningsToExceptions="true"
12+
processIsolation="false"
13+
stopOnFailure="false"
14+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
15+
>
16+
<coverage>
17+
<include>
18+
<directory suffix=".php">src/</directory>
19+
</include>
20+
</coverage>
21+
<testsuites>
22+
<testsuite name="Unit">
23+
<directory suffix="Test.php">./tests/Unit</directory>
24+
</testsuite>
25+
<testsuite name="Feature">
26+
<directory suffix="Test.php">./tests/Feature</directory>
27+
</testsuite>
28+
</testsuites>
29+
<php>
30+
<env name="DB_CONNECTION" value="testing"/>
31+
</php>
32+
</phpunit>

src/ExportPostman.php renamed to src/Commands/ExportPostmanCommand.php

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<?php
22

3-
namespace AndreasElia\PostmanGenerator;
3+
namespace AndreasElia\PostmanGenerator\Commands;
44

55
use Closure;
66
use Illuminate\Console\Command;
77
use Illuminate\Contracts\Config\Repository;
88
use Illuminate\Foundation\Http\FormRequest;
99
use Illuminate\Routing\Router;
1010
use Illuminate\Support\Facades\Storage;
11+
use Illuminate\Support\Str;
1112
use ReflectionClass;
1213
use ReflectionFunction;
1314

14-
class ExportPostman extends Command
15+
class ExportPostmanCommand extends Command
1516
{
1617
/** @var string */
1718
protected $signature = 'export:postman {--bearer= : The bearer token to use on your endpoints}';
@@ -28,34 +29,35 @@ class ExportPostman extends Command
2829
/** @var array */
2930
protected $config;
3031

32+
/** @var null */
33+
protected $filename;
34+
3135
public function __construct(Router $router, Repository $config)
3236
{
3337
parent::__construct();
3438

3539
$this->router = $router;
3640
$this->config = $config['api-postman'];
41+
$this->filename = $this->formatFilename();
3742
}
3843

3944
public function handle(): void
4045
{
41-
$bearer = $this->option('bearer') ?? false;
42-
43-
$filename = date('Y_m_d_His').'_postman';
44-
45-
$this->initStructure($filename);
46+
$this->initStructure();
4647

47-
if ($bearer) {
48+
if ($bearer = $this->option('bearer') ?? false) {
4849
$this->structure['variable'][] = [
4950
'key' => 'token',
5051
'value' => $bearer,
5152
];
5253
}
5354

5455
foreach ($this->router->getRoutes() as $route) {
55-
$middleware = $route->middleware();
56+
$methods = collect($route->methods())->reject(fn ($method) => $method == 'HEAD');
57+
$middleware = $route->gatherMiddleware();
5658

57-
foreach ($route->methods as $method) {
58-
if ($method == 'HEAD' || empty($middleware) || $middleware[0] !== 'api') {
59+
foreach ($methods as $method) {
60+
if (empty($middleware) || ! in_array('api', $middleware)) {
5961
continue;
6062
}
6163

@@ -95,9 +97,9 @@ public function handle(): void
9597
];
9698
}
9799

98-
$request = $this->makeItem($route, $method, $routeHeaders, $requestRules);
100+
$request = $this->makeRequest($route, $method, $routeHeaders, $requestRules);
99101

100-
if ($this->config['structured']) {
102+
if ($this->isStructured()) {
101103
$routeNames = $route->action['as'] ?? null;
102104

103105
if (! $routeNames) {
@@ -114,23 +116,22 @@ public function handle(): void
114116
return ! is_null($value) && $value !== '';
115117
});
116118

117-
$destination = end($routeNames);
118-
119-
$this->ensurePath($this->structure, $routeNames, $request, $destination);
119+
$this->buildTree($this->structure, $routeNames, $request);
120120
} else {
121121
$this->structure['item'][] = $request;
122122
}
123123
}
124124
}
125125

126-
Storage::put($exportName = "$filename.json", json_encode($this->structure));
126+
Storage::put($exportName = "postman/$this->filename", json_encode($this->structure));
127127

128128
$this->info("Postman Collection Exported: $exportName");
129129
}
130130

131-
protected function ensurePath(array &$routes, array $segments, array $request, string $destination): void
131+
protected function buildTree(array &$routes, array $segments, array $request): void
132132
{
133133
$parent = &$routes;
134+
$destination = end($segments);
134135

135136
foreach ($segments as $segment) {
136137
$matched = false;
@@ -165,7 +166,7 @@ protected function ensurePath(array &$routes, array $segments, array $request, s
165166
}
166167
}
167168

168-
public function makeItem($route, $method, $routeHeaders, $requestRules)
169+
public function makeRequest($route, $method, $routeHeaders, $requestRules)
169170
{
170171
$data = [
171172
'name' => $route->uri(),
@@ -199,7 +200,7 @@ public function makeItem($route, $method, $routeHeaders, $requestRules)
199200
return $data;
200201
}
201202

202-
protected function initStructure(string $filename): void
203+
protected function initStructure(): void
203204
{
204205
$this->structure = [
205206
'variable' => [
@@ -209,10 +210,24 @@ protected function initStructure(string $filename): void
209210
],
210211
],
211212
'info' => [
212-
'name' => $filename,
213+
'name' => $this->filename,
213214
'schema' => 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json',
214215
],
215216
'item' => [],
216217
];
217218
}
219+
220+
protected function formatFilename()
221+
{
222+
return str_replace(
223+
['{timestamp}', '{app}'],
224+
[date('Y_m_d_His'), Str::snake(config('app.name'))],
225+
$this->config['filename']
226+
);
227+
}
228+
229+
protected function isStructured()
230+
{
231+
return $this->config['structured'];
232+
}
218233
}

src/PostmanGeneratorServiceProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace AndreasElia\PostmanGenerator;
44

5+
use AndreasElia\PostmanGenerator\Commands\ExportPostmanCommand;
56
use Illuminate\Support\ServiceProvider;
67

78
class PostmanGeneratorServiceProvider extends ServiceProvider
@@ -19,7 +20,7 @@ public function boot()
1920
], 'postman-config');
2021
}
2122

22-
$this->commands(ExportPostman::class);
23+
$this->commands(ExportPostmanCommand::class);
2324
}
2425

2526
/**

tests/Feature/ExportPostmanTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace AndreasElia\PostmanGenerator\Tests\Feature;
4+
5+
use AndreasElia\PostmanGenerator\Tests\TestCase;
6+
use Illuminate\Support\Arr;
7+
use Illuminate\Support\Facades\Storage;
8+
9+
class ExportPostmanTest extends TestCase
10+
{
11+
protected function setUp(): void
12+
{
13+
parent::setUp();
14+
15+
config()->set('api-postman.filename', 'test.json');
16+
17+
Storage::disk()->deleteDirectory('postman');
18+
}
19+
20+
public function test_standard_export_works()
21+
{
22+
$this->artisan('export:postman')->assertExitCode(0);
23+
24+
$collection = json_decode(Storage::get('postman/'.config('api-postman.filename')), true);
25+
26+
$routes = $this->app['router']->getRoutes();
27+
28+
$collectionItems = $collection['item'];
29+
30+
$this->assertCount(count($routes), $collectionItems);
31+
foreach ($routes as $route) {
32+
$collectionRoute = Arr::first($collectionItems, function ($item) use ($route) {
33+
return $item['name'] == $route->uri();
34+
});
35+
36+
$this->assertNotNull($collectionRoute);
37+
$this->assertTrue(in_array($collectionRoute['request']['method'], $route->methods()));
38+
}
39+
}
40+
41+
public function test_bearer_export_works()
42+
{
43+
$this->artisan('export:postman --bearer=1234567890')->assertExitCode(0);
44+
45+
$collection = json_decode(Storage::get('postman/'.config('api-postman.filename')), true);
46+
47+
$routes = $this->app['router']->getRoutes();
48+
49+
$collectionVariables = $collection['variable'];
50+
51+
foreach ($collectionVariables as $variable) {
52+
if ($variable['key'] != 'token') {
53+
continue;
54+
}
55+
56+
$this->assertEquals($variable['value'], '1234567890');
57+
}
58+
59+
$this->assertCount(2, $collectionVariables);
60+
61+
$collectionItems = $collection['item'];
62+
63+
$this->assertCount(count($routes), $collectionItems);
64+
65+
foreach ($routes as $route) {
66+
$collectionRoute = Arr::first($collectionItems, function ($item) use ($route) {
67+
return $item['name'] == $route->uri();
68+
});
69+
70+
$this->assertNotNull($collectionRoute);
71+
$this->assertTrue(in_array($collectionRoute['request']['method'], $route->methods()));
72+
}
73+
}
74+
75+
public function test_structured_export_works()
76+
{
77+
config()->set('api-postman.structured', true);
78+
79+
$this->artisan('export:postman')
80+
->assertExitCode(0);
81+
82+
$this->assertTrue(true);
83+
84+
$collection = json_decode(Storage::get('postman/'.config('api-postman.filename')), true);
85+
86+
$routes = $this->app['router']->getRoutes();
87+
88+
$collectionItems = $collection['item'];
89+
90+
$this->assertCount(count($routes), $collectionItems[0]['item']);
91+
}
92+
}

tests/Fixtures/ExampleController.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace AndreasElia\PostmanGenerator\Tests\Fixtures;
4+
5+
use Illuminate\Routing\Controller;
6+
7+
class ExampleController extends Controller
8+
{
9+
public function index()
10+
{
11+
return 'index';
12+
}
13+
14+
public function show()
15+
{
16+
return 'show';
17+
}
18+
19+
public function store()
20+
{
21+
return 'store';
22+
}
23+
24+
public function delete()
25+
{
26+
return 'delete';
27+
}
28+
}

0 commit comments

Comments
 (0)