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