Completed
Branch v4 (b2a7ba)
by Pieter
04:35
created

IlluminatePlugin::getExampleUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
namespace W2w\Laravel\Apie\Plugins\Illuminate;
3
4
use erasys\OpenApi\Spec\v3\Contact;
5
use erasys\OpenApi\Spec\v3\Document;
6
use erasys\OpenApi\Spec\v3\Info;
7
use erasys\OpenApi\Spec\v3\License;
8
use erasys\OpenApi\Spec\v3\Schema;
9
use Illuminate\Auth\Authenticatable;
10
use Illuminate\Container\Container;
11
use Illuminate\Contracts\Translation\Translator;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Http\Request;
14
use Illuminate\Support\Collection;
15
use Illuminate\Support\LazyCollection;
0 ignored issues
show
Bug introduced by
The type Illuminate\Support\LazyCollection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Symfony\Component\Serializer\Encoder\EncoderInterface;
17
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
18
use W2w\Laravel\Apie\Events\OpenApiSpecGenerated;
19
use W2w\Laravel\Apie\Plugins\Illuminate\Encoders\DefaultContentTypeFormatRetriever;
20
use W2w\Laravel\Apie\Plugins\Illuminate\Normalizers\CollectionNormalizer;
21
use W2w\Laravel\Apie\Plugins\Illuminate\Normalizers\LazyCollectionNormalizer;
22
use W2w\Laravel\Apie\Plugins\Illuminate\ObjectAccess\AuthObjectAccess;
23
use W2w\Laravel\Apie\Plugins\Illuminate\ResourceFactories\FromIlluminateContainerFactory;
24
use W2w\Laravel\Apie\Plugins\Illuminate\Schema\CollectionSchemaBuilder;
25
use W2w\Laravel\Apie\Providers\ApieConfigResolver;
26
use W2w\Lib\Apie\Core\ApiResourceMetadataFactory;
27
use W2w\Lib\Apie\Core\ClassResourceConverter;
28
use W2w\Lib\Apie\Core\IdentifierExtractor;
29
use W2w\Lib\Apie\Core\Resources\ApiResourcesInterface;
30
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterRequest;
31
use W2w\Lib\Apie\Exceptions\InvalidClassTypeException;
32
use W2w\Lib\Apie\Interfaces\ApiResourceFactoryInterface;
33
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface;
34
use W2w\Lib\Apie\PluginInterfaces\ApieConfigInterface;
35
use W2w\Lib\Apie\PluginInterfaces\ApiResourceFactoryProviderInterface;
36
use W2w\Lib\Apie\PluginInterfaces\EncoderProviderInterface;
37
use W2w\Lib\Apie\PluginInterfaces\FrameworkConnectionInterface;
38
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
39
use W2w\Lib\Apie\PluginInterfaces\ObjectAccessProviderInterface;
40
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
41
use W2w\Lib\Apie\PluginInterfaces\OpenApiInfoProviderInterface;
42
use W2w\Lib\Apie\PluginInterfaces\ResourceProviderInterface;
43
use W2w\Lib\Apie\PluginInterfaces\SchemaProviderInterface;
44
use W2w\Lib\Apie\PluginInterfaces\SubActionsProviderInterface;
45
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccessInterface;
46
47
class IlluminatePlugin implements ObjectAccessProviderInterface, ResourceProviderInterface, ApieConfigInterface, OpenApiInfoProviderInterface, ApiResourceFactoryProviderInterface, EncoderProviderInterface, NormalizerProviderInterface, OpenApiEventProviderInterface, SchemaProviderInterface, SubActionsProviderInterface, FrameworkConnectionInterface
48
{
49
    private $container;
50
51
    private $resolvedConfig;
52
53
    public function __construct(Container $container, array $config)
54
    {
55
        $this->container = $container;
56
        $this->resolvedConfig = ApieConfigResolver::resolveConfig($config);
57
    }
58
59
    public function getLaravelConfig(): array
60
    {
61
        return $this->resolvedConfig;
62
    }
63
64
    /**
65
     * Returns a list of Api resources.
66
     *
67
     * @return string[]
68
     */
69
    public function getResources(): array
70
    {
71
        if (!empty($this->resolvedConfig['resources-service'])) {
72
            $resources = $this->container->make($this->resolvedConfig['resources-service']);
73
            if (!($resources instanceof ApiResourcesInterface)) {
74
                throw new InvalidClassTypeException('resources-service', ApiResourcesInterface::class);
75
            }
76
            return $resources->getApiResources();
77
        }
78
        return $this->resolvedConfig['resources'];
79
    }
80
81
    /**
82
     * {@inheritDoc}
83
     */
84
    public function getBaseUrl(): string
85
    {
86
        $baseUrl = $this->resolvedConfig['base-url'] . $this->resolvedConfig['api-url'];
87
        if ($this->container->has(Request::class)) {
88
            $baseUrl = $this->container->get(Request::class)->getSchemeAndHttpHost() . $baseUrl;
89
        }
90
        return $baseUrl;
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96
    public function createInfo(): Info
97
    {
98
        return new Info(
99
            $this->resolvedConfig['metadata']['title'],
100
            $this->resolvedConfig['metadata']['version'],
101
            $this->resolvedConfig['metadata']['description'],
102
            [
103
                'contact' => new Contact([
104
                    'name'  => $this->resolvedConfig['metadata']['contact-name'],
105
                    'url'   => $this->resolvedConfig['metadata']['contact-url'],
106
                    'email' => $this->resolvedConfig['metadata']['contact-email'],
107
                ]),
108
                'license' => new License(
109
                    $this->resolvedConfig['metadata']['license'],
110
                    $this->resolvedConfig['metadata']['license-url']
111
                ),
112
            ]
113
        );
114
    }
115
116
    /**
117
     * {@inheritDoc}
118
     */
119
    public function getApiResourceFactory(): ApiResourceFactoryInterface
120
    {
121
        return new FromIlluminateContainerFactory($this->container);
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127
    public function getEncoders(): array
128
    {
129
        $res = [];
130
        // container->tagged has hazy return value...
131
        foreach ($this->container->tagged(EncoderInterface::class) as $normalizer) {
132
            $res[] = $normalizer;
133
        };
134
        return $res;
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140
    public function getFormatRetriever(): FormatRetrieverInterface
141
    {
142
        return new DefaultContentTypeFormatRetriever();
143
    }
144
145
    /**
146
     * {@inheritDoc}
147
     */
148
    public function getNormalizers(): array
149
    {
150
        $res = [];
151
        // container->tagged has hazy return value...
152
        foreach ($this->container->tagged(NormalizerInterface::class) as $normalizer) {
153
            $res[] = $normalizer;
154
        };
155
        $res[] = new CollectionNormalizer();
156
        if (class_exists(LazyCollection::class)) {
157
            $res[] = new LazyCollectionNormalizer();
158
        }
159
        return $res;
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function onOpenApiDocGenerated(Document $document): Document
166
    {
167
        $event = new OpenApiSpecGenerated($document);
168
        $this->container->get('events')->dispatch($event);
169
        return $event->getDocument();
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175
    public function getDefinedStaticData(): array
176
    {
177
        return [
178
            Model::class => new Schema([
179
                'type' => 'object'
180
            ]),
181
        ];
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187
    public function getDynamicSchemaLogic(): array
188
    {
189
        $schemas = [
190
            Collection::class => new CollectionSchemaBuilder(),
191
        ];
192
        if (class_exists(LazyCollection::class)) {
193
            $schemas[LazyCollection::class] = new CollectionSchemaBuilder();
194
        }
195
        return $schemas;
196
    }
197
198
    /**
199
     * {@inheritDoc}
200
     */
201
    public function getSubActions()
202
    {
203
        $subActions = $this->resolvedConfig['subactions'];
204
        $results = [];
205
        foreach ($subActions as $slug => $actions) {
206
            $results[$slug] = [];
207
            foreach ($actions as $action) {
208
                $results[$slug][] = $this->container->make($action);
209
            }
210
        }
211
        return $results;
212
    }
213
214
    /**
215
     * {@inheritDoc}
216
     */
217
    public function getObjectAccesses(): array
218
    {
219
        $objectAccess = $this->resolvedConfig['object-access'];
220
        $results = [
221
            Authenticatable::class => new AuthObjectAccess(),
222
        ];
223
        foreach ($objectAccess as $key => $objectAccessClass) {
224
            $service = $this->container->make($objectAccessClass);
225
            if (!($service instanceof ObjectAccessInterface)) {
226
                throw new InvalidClassTypeException($objectAccessClass, 'ObjectAccessInterface');
227
            }
228
            $results[$key] = $service;
229
        }
230
        return $results;
231
    }
232
233
    public function getService(string $id): object
234
    {
235
        return $this->container->make($id);
236
    }
237
238
    public function getUrlForResource(object $resource): ?string
239
    {
240
        return null;
241
        $baseUrl = $this->getBaseUrl();
0 ignored issues
show
Unused Code introduced by
$baseUrl = $this->getBaseUrl() is not 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...
242
        /**
243
         * @var ClassResourceConverter
244
         */
245
        $classResourceConverter = $this->container->make(ClassResourceConverter::class);
246
        /**
247
         * @var IdentifierExtractor
248
         */
249
        $identifierExtractor = $this->container->make(IdentifierExtractor::class);
250
        /**
251
         * @var ApiResourceMetadataFactory
252
         */
253
        $apiMetadataFactory = $this->container->make(ApiResourceMetadataFactory::class);
254
        $metadata = $apiMetadataFactory->getMetadata($resource);
255
        $identifier = $identifierExtractor->getIdentifierValue($resource, $metadata->getContext());
256
        if (!$identifier || !$metadata->allowGet()) {
257
            return null;
258
        }
259
        return $this->getBaseUrl() . '/' . $classResourceConverter->normalize($metadata->getClassName()) . '/' . $identifier;
260
    }
261
262
    public function getOverviewUrlForResourceClass(string $resourceClass, ?SearchFilterRequest $filterRequest = null
263
    ): ?string {
264
        return null;
265
        /**
266
         * @var ClassResourceConverter
267
         */
268
        $classResourceConverter = $this->container->make(ClassResourceConverter::class);
0 ignored issues
show
Unused Code introduced by
$classResourceConverter ...sourceConverter::class) is not 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...
269
        /**
270
         * @var ApiResourceMetadataFactory
271
         */
272
        $apiMetadataFactory = $this->container->make(ApiResourceMetadataFactory::class);
273
        $metadata = $apiMetadataFactory->getMetadata($resourceClass);
274
        if (!$metadata->allowGetAll()) {
275
            return null;
276
        }
277
        $query = '';
278
        if ($filterRequest) {
279
            $searchQuery = $filterRequest->getSearches();
280
            $searchQuery['page'] = $filterRequest->getPageIndex();
281
            $searchQuery['limit'] = $filterRequest->getNumberOfItems();
282
            $query = '?' . http_build_query($searchQuery);
283
        }
284
        return $this->getBaseUrl() . '/' . $classResourceConverter->normalize($metadata->getClassName()) . $query;
285
    }
286
287
    public function getExampleUrl(string $resourceClass): ?string
288
    {
289
        return null;
290
    }
291
292
    public function getAcceptLanguage(): ?string
293
    {
294
        return $this->container->get(Translator::class)->getLocale();
295
    }
296
297
    public function getContentLanguage(): ?string
298
    {
299
        if ($this->container->has(Request::class)) {
300
            /** @var Request $request */
301
            $request = $this->container->get(Request::class);
302
            return $request->header('Content-Language', $this->getAcceptLanguage());
303
        }
304
        return null;
305
    }
306
}
307