Completed
Push — master ( e7cbd3...371b43 )
by
unknown
01:58 queued 11s
created

PostmanCollectionWriter::makeUrlData()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 9.216
c 0
b 0
f 0
cc 3
nc 2
nop 1
1
<?php
2
3
namespace Mpociot\ApiDoc\Writing;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\URL;
7
use Illuminate\Support\Str;
8
use Ramsey\Uuid\Uuid;
9
use ReflectionMethod;
10
11
class PostmanCollectionWriter
12
{
13
    /**
14
     * @var Collection
15
     */
16
    private $routeGroups;
17
18
    /**
19
     * @var string
20
     */
21
    private $baseUrl;
22
23
    /**
24
     * @var string
25
     */
26
    private $protocol;
27
28
    /**
29
     * @var array|null
30
     */
31
    private $auth;
32
33
    /**
34
     * CollectionWriter constructor.
35
     *
36
     * @param Collection $routeGroups
37
     */
38
    public function __construct(Collection $routeGroups, $baseUrl)
39
    {
40
        $this->routeGroups = $routeGroups;
41
        $this->protocol = Str::startsWith($baseUrl, 'https') ? 'https' : 'http';
42
        $this->baseUrl = $this->getBaseUrl($baseUrl);
43
        $this->auth = config('apidoc.postman.auth');
44
    }
45
46
    public function getCollection()
47
    {
48
        $collection = [
49
            'variables' => [],
50
            'info' => [
51
                'name' => config('apidoc.postman.name') ?: config('app.name').' API',
52
                '_postman_id' => Uuid::uuid4()->toString(),
53
                'description' => config('apidoc.postman.description') ?: '',
54
                'schema' => 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json',
55
            ],
56
            'item' => $this->routeGroups->map(function (Collection $routes, $groupName) {
57
                return [
58
                    'name' => $groupName,
59
                    'description' => $routes->first()['metadata']['groupDescription'],
60
                    'item' => $routes->map(\Closure::fromCallable([$this, 'generateEndpointItem']))->toArray(),
61
                ];
62
            })->values()->toArray(),
63
        ];
64
65
        if (! empty($this->auth)) {
66
            $collection['auth'] = $this->auth;
67
        }
68
69
        return json_encode($collection, JSON_PRETTY_PRINT);
70
    }
71
72
    protected function generateEndpointItem($route)
73
    {
74
        $mode = 'raw';
75
76
        $method = $route['methods'][0];
77
78
        return [
79
            'name' => $route['metadata']['title'] != '' ? $route['metadata']['title'] : $route['uri'],
80
            'request' => [
81
                'url' => $this->makeUrlData($route),
82
                'method' => $method,
83
                'header' => $this->resolveHeadersForRoute($route),
84
                'body' => [
85
                    'mode' => $mode,
86
                    $mode => json_encode($route['cleanBodyParameters'], JSON_PRETTY_PRINT),
87
                ],
88
                'description' => $route['metadata']['description'] ?? null,
89
                'response' => [],
90
            ],
91
        ];
92
    }
93
94
    protected function resolveHeadersForRoute($route)
95
    {
96
        $headers = collect($route['headers']);
97
98
        // Exclude authentication headers if they're handled by Postman auth
99
        $authHeader = $this->getAuthHeader();
100
        if (! empty($authHeader)) {
101
            $headers = $headers->except($authHeader);
102
        }
103
104
        return $headers
105
            ->union([
106
                'Accept' => 'application/json',
107
            ])
108
            ->map(function ($value, $header) {
109
                return [
110
                    'key' => $header,
111
                    'value' => $value,
112
                ];
113
            })
114
            ->values()
115
            ->all();
116
    }
117
118
    protected function makeUrlData($route)
119
    {
120
        // URL Parameters are collected by the `UrlParameters` strategies, but only make sense if they're in the route
121
        // definition. Filter out any URL parameters that don't appear in the URL.
122
        $urlParams = collect($route['urlParameters'])->filter(function ($_, $key) use ($route) {
123
            return Str::contains($route['uri'], '{'.$key.'}');
124
        });
125
126
        /** @var Collection $queryParams */
127
        $base = [
128
            'protocol' => $this->protocol,
129
            'host' => $this->baseUrl,
130
            // Substitute laravel/symfony query params ({example}) to Postman style, prefixed with a colon
131
            'path' => preg_replace_callback('/\/{(\w+)\??}(?=\/|$)/', function ($matches) {
132
                return '/:'.$matches[1];
133
            }, $route['uri']),
134
            'query' => collect($route['queryParameters'])->map(function ($parameter, $key) {
135
                return [
136
                    'key' => $key,
137
                    'value' => urlencode($parameter['value']),
138
                    'description' => $parameter['description'],
139
                    // Default query params to disabled if they aren't required and have empty values
140
                    'disabled' => ! $parameter['required'] && empty($parameter['value']),
141
                ];
142
            })->values()->toArray(),
143
        ];
144
145
        // If there aren't any url parameters described then return what we've got
146
        /** @var $urlParams Collection */
147
        if ($urlParams->isEmpty()) {
148
            return $base;
149
        }
150
151
        $base['variable'] = $urlParams->map(function ($parameter, $key) {
152
            return [
153
                'id' => $key,
154
                'key' => $key,
155
                'value' => urlencode($parameter['value']),
156
                'description' => $parameter['description'],
157
            ];
158
        })->values()->toArray();
159
160
        return $base;
161
    }
162
163
    protected function getAuthHeader()
164
    {
165
        $auth = $this->auth;
166
        if (empty($auth) || ! is_string($auth['type'] ?? null)) {
167
            return null;
168
        }
169
170
        switch ($auth['type']) {
171
            case 'bearer':
172
                return 'Authorization';
173
            case 'apikey':
174
                $spec = $auth['apikey'];
175
176
                if (isset($spec['in']) && $spec['in'] !== 'header') {
177
                    return null;
178
                }
179
180
                return $spec['key'];
181
            default:
182
                return null;
183
        }
184
    }
185
186
    protected function getBaseUrl($baseUrl)
187
    {
188
        if (Str::contains(app()->version(), 'Lumen')) { //Is Lumen
189
            $reflectionMethod = new ReflectionMethod(\Laravel\Lumen\Routing\UrlGenerator::class, 'getRootUrl');
190
            $reflectionMethod->setAccessible(true);
191
            $url = app('url');
192
193
            return $reflectionMethod->invokeArgs($url, ['', $baseUrl]);
194
        }
195
196
        return URL::formatRoot('', $baseUrl);
197
    }
198
}
199