Passed
Branch feature-cache-bridge-optional (cb5811)
by Pieter
03:35
created

ApiResourceServiceProvider   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Importance

Changes 17
Bugs 3 Features 7
Metric Value
eloc 117
c 17
b 3
f 7
dl 0
loc 214
rs 10
wmc 16

6 Methods

Rating   Name   Duplication   Size   Complexity  
A boot() 0 11 2
A addStatusResourceServices() 0 8 1
A addOpenApiServices() 0 31 1
A handleSerializerCache() 0 7 3
B register() 0 92 5
A createServiceLibraryFactory() 0 40 4
1
<?php
2
3
namespace W2w\Laravel\Apie\Providers;
4
5
use DateTimeInterface;
6
use erasys\OpenApi\Spec\v3\Contact;
7
use erasys\OpenApi\Spec\v3\Info;
8
use erasys\OpenApi\Spec\v3\License;
9
use erasys\OpenApi\Spec\v3\Schema;
10
use Illuminate\Contracts\Cache\Repository as CacheRepository;
11
use Illuminate\Contracts\Cache\Repository;
12
use Illuminate\Http\Request;
13
use Illuminate\Support\ServiceProvider;
14
use Madewithlove\IlluminatePsrCacheBridge\Laravel\CacheItemPool;
0 ignored issues
show
Bug introduced by
The type Madewithlove\IlluminateP...e\Laravel\CacheItemPool 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...
15
use Ramsey\Uuid\Uuid;
16
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17
use Symfony\Component\OptionsResolver\OptionsResolver;
18
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
19
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
20
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
21
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
22
use Symfony\Component\Serializer\Serializer;
23
use Symfony\Component\Serializer\SerializerInterface;
24
use W2w\Laravel\Apie\Services\Retrievers\DatabaseQueryRetriever;
25
use W2w\Laravel\Apie\Services\Retrievers\EloquentModelDataLayer;
26
use W2w\Lib\Apie\ApiResourceFacade;
27
use W2w\Lib\Apie\ApiResourceFactory;
28
use W2w\Lib\Apie\IdentifierExtractor;
29
use W2w\Lib\Apie\Mocks\MockApiResourceDataLayer;
30
use W2w\Lib\Apie\Mocks\MockApiResourceFactory;
31
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator;
32
use W2w\Lib\Apie\OpenApiSchema\SchemaGenerator;
33
use W2w\Lib\Apie\Resources\ApiResources;
34
use W2w\Lib\Apie\Resources\ApiResourcesInterface;
35
use W2w\Lib\Apie\Retrievers\ApplicationInfoRetriever;
36
use W2w\Lib\Apie\Retrievers\FileStorageDataLayer;
37
use W2w\Lib\Apie\Retrievers\StatusCheckRetriever;
38
use W2w\Laravel\Apie\Services\StatusChecks\StatusFromDatabaseRetriever;
39
use W2w\Lib\Apie\ServiceLibraryFactory;
40
41
/**
42
 * Install apie classes to Laravel.
43
 */
44
class ApiResourceServiceProvider extends ServiceProvider
45
{
46
    /**
47
     * Perform post-registration booting of services.
48
     *
49
     * @return void
50
     */
51
    public function boot()
52
    {
53
        if (function_exists('config_path')) {
54
            $this->publishes(
55
                [
56
                    __DIR__ . '/../../config/apie.php' => config_path('apie.php'),
57
                ]
58
            );
59
        }
60
61
        $this->loadMigrationsFrom(__DIR__ . '/../../migrations');
62
    }
63
64
    /**
65
     * Register any application services.
66
     */
67
    public function register()
68
    {
69
        $this->app->singleton('apie.config', function () {
70
            $config = $this->app->get('config');
71
72
            $resolver = new OptionsResolver();
73
            $defaults = require __DIR__ . '/../../config/apie.php';
74
            $resolver->setDefaults($defaults);
75
            return $resolver->resolve($config->get('apie') ?? []);
76
        });
77
78
        $this->app->singleton(ApiResourcesInterface::class, function () {
79
            $config = $this->app->get('apie.config');
80
            if (! empty($config['resources-service'])) {
81
                return $this->app->get($config['resources-service']);
82
            }
83
            if ($config['resources'] instanceof ApiResourcesInterface) {
84
                return $config['resources'];
85
            }
86
            return new ApiResources($config['resources']);
87
        });
88
89
        $this->app->singleton(ServiceLibraryFactory::class, function () {
90
            $this->createServiceLibraryFactory();
91
        });
92
93
        // MainScheduler: service that does all the background processes of api resources.
94
        //$this->app->singleton(MainScheduler::class, function () {
95
        //    return new MainScheduler($this->app->tagged(SchedulerInterface::class));
96
        //});
97
98
        // OpenApiSpecGenerator: generated an OpenAPI 3.0 spec file from a list of resources.
99
        $this->addOpenApiServices();
100
        $this->app->singleton(OpenApiSpecGenerator::class, function () {
101
            $config = $this->app->get('apie.config');
102
            $factory = $this->app->get(ServiceLibraryFactory::class);
103
            $baseUrl = $config['base-url'] . $config['api-url'];
104
            if ($this->app->has(Request::class)) {
105
                $baseUrl = $this->app->get(Request::class)->getSchemeAndHttpHost() . $baseUrl;
106
            }
107
            $this->app->get(SchemaGenerator::class);
108
            $factory->setInfo($this->app->get(Info::class));
109
            return $factory->getOpenApiSpecGenerator($baseUrl);
110
        });
111
112
        // SchemaGenerator: generates a OpenAPI Schema from a api resource class.
113
        $this->app->singleton(SchemaGenerator::class, function () {
114
            $factory = $this->app->get(ServiceLibraryFactory::class);
115
            $service = $factory->getSchemaGenerator();
116
            $service->defineSchemaForResource(Uuid::class, new Schema(['type' => 'string', 'format' => 'uuid']));
117
            $service->defineSchemaForResource(DateTimeInterface::class, new Schema(['type' => 'string', 'format' => 'date-time']));
118
            return $service;
119
        });
120
121
        $this->app->singleton(Serializer::class, function () {
122
            return $this->app->get(ServiceLibraryFactory::class)->getSerializer();
123
        });
124
        $this->app->bind(SerializerInterface::class, Serializer::class);
125
        $this->app->bind(NormalizerInterface::class, Serializer::class);
126
        $this->app->bind(DenormalizerInterface::class, Serializer::class);
127
        $this->app->singleton(CamelCaseToSnakeCaseNameConverter::class);
128
        $this->app->bind(NameConverterInterface::class, CamelCaseToSnakeCaseNameConverter::class);
129
130
        $this->app->singleton(ApplicationInfoRetriever::class, function () {
131
            $config = $this->app->get('apie.config');
132
            return new ApplicationInfoRetriever(
133
                config('app.name'),
134
                config('app.env'),
135
                $config['metadata']['hash'],
136
                config('app.debug')
137
            );
138
        });
139
        $this->app->singleton(EloquentModelDataLayer::class);
140
        $this->app->singleton(DatabaseQueryRetriever::class);
141
        $this->app->singleton(FileStorageDataLayer::class, function () {
142
            return new FileStorageDataLayer(
143
                storage_path('api-file-storage'),
144
                $this->app->get(ServiceLibraryFactory::class)->getPropertyAccessor()
145
            );
146
        });
147
148
        // ApiResourceFacade: class that calls all the right services with a simple interface.
149
        $this->app->singleton(ApiResourceFacade::class, function () {
150
            return $this->app->get(ServiceLibraryFactory::class)->getApiResourceFacade();
151
        });
152
153
        $this->addStatusResourceServices();
154
155
        if (strpos($this->app->version(), 'Lumen') === false) {
156
            $this->app->register(ApieLaravelServiceProvider::class);
157
        } else {
158
            $this->app->register(ApieLumenServiceProvider::class);
159
        }
160
    }
161
162
    private function addOpenApiServices()
163
    {
164
        // Provides contact information to the OpenAPI spec.
165
        $this->app->singleton(Contact::class, function () {
166
            $config = $this->app->get('apie.config');
167
            return new Contact([
168
                'name'  => $config['metadata']['contact-name'],
169
                'url'   => $config['metadata']['contact-url'],
170
                'email' => $config['metadata']['contact-email'],
171
            ]);
172
        });
173
174
        // Provides license information to the OpenAPI spec.
175
        $this->app->singleton(License::class, function () {
176
            $config = $this->app->get('apie.config');
177
            return new License(
178
                $config['metadata']['license'],
179
                $config['metadata']['license-url']
180
            );
181
        });
182
183
        // Provides OpenAPI info to the OpenAPI spec.
184
        $this->app->singleton(Info::class, function () {
185
            $config = $this->app->get('apie.config');
186
            return new Info(
187
                $config['metadata']['title'],
188
                $config['metadata']['version'],
189
                $config['metadata']['description'],
190
                [
191
                    'contact' => $this->app->get(Contact::class),
192
                    'license' => $this->app->get(License::class),
193
                ]
194
            );
195
        });
196
    }
197
198
    private function createServiceLibraryFactory(): ServiceLibraryFactory
199
    {
200
        $config = $this->app->get('apie.config');
201
        $result = new ServiceLibraryFactory(
202
            $this->app->get(ApiResourcesInterface::class),
203
            (bool) config('app.debug'),
204
            storage_path('apie-cache')
205
        );
206
        $result->setContainer($this->app);
207
        if (!config('app.debug')) {
208
            $this->handleSerializerCache($result);
209
        }
210
        $result->runBeforeInstantiation(function () use (&$result) {
211
            $normalizers = [
212
            ];
213
            $taggedNormalizers = $this->app->tagged(NormalizerInterface::class);
214
            // app->tagged return type is hazy....
215
            foreach ($taggedNormalizers as $taggedNormalizer) {
216
                $normalizers[] = $taggedNormalizer;
217
            }
218
            $result->setAdditionalNormalizers($normalizers);
219
        });
220
221
        if ($config['mock']) {
222
            $cachePool = $result->getSerializerCache();
223
224
            $result->setApiResourceFactory(
225
                new MockApiResourceFactory(
226
                    new MockApiResourceDataLayer(
227
                        $cachePool,
228
                        new IdentifierExtractor($result->getPropertyAccessor()),
229
                        $result->getPropertyAccessor()
230
                    ),
231
                    new ApiResourceFactory($this->app),
232
                    $config['mock-skipped-resources']
233
                )
234
            );
235
        }
236
237
        return $result;
238
    }
239
240
    private function handleSerializerCache(ServiceLibraryFactory $result)
241
    {
242
        if ($this->app->bound('cache.psr6')) {
243
            $result->setSerializerCache($this->app->get('cache.psr6'));
244
        } elseif (class_exists(CacheItemPool::class)) {
245
            $repository = $this->app->make(Repository::class);
246
            $result->setSerializerCache(new CacheItemPool($repository));
247
        }
248
    }
249
250
    private function addStatusResourceServices()
251
    {
252
        $this->app->singleton(StatusFromDatabaseRetriever::class, function () {
253
            return new StatusFromDatabaseRetriever(config('app.debug'));
254
        });
255
        $this->app->tag([StatusFromDatabaseRetriever::class], ['status-check']);
256
        $this->app->singleton(StatusCheckRetriever::class, function () {
257
            return new StatusCheckRetriever($this->app->tagged('status-check'));
258
        });
259
    }
260
}
261