Completed
Push — develop ( 390d04...c734df )
by Neomerx
03:03
created

FluteSettings::getFormSerializedRules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php namespace Limoncello\Flute\Package;
2
3
use Limoncello\Common\Reflection\ClassIsTrait;
4
use Limoncello\Contracts\Application\ApplicationConfigurationInterface as A;
5
use Limoncello\Contracts\Settings\Packages\FluteSettingsInterface;
6
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
7
use Limoncello\Flute\Contracts\Validation\FormRulesInterface;
8
use Limoncello\Flute\Contracts\Validation\JsonApiDataRulesInterface;
9
use Limoncello\Flute\Contracts\Validation\JsonApiQueryRulesInterface;
10
use Limoncello\Flute\L10n\Messages;
11
use Limoncello\Flute\Validation\Form\Execution\FormRulesSerializer;
12
use Limoncello\Flute\Validation\JsonApi\Execution\JsonApiDataRulesSerializer;
13
use Limoncello\Flute\Validation\JsonApi\Execution\JsonApiQueryRulesSerializer;
14
use Limoncello\Flute\Validation\JsonApi\Rules\DefaultQueryValidationRules;
15
use Limoncello\Validation\Execution\BlockSerializer;
16
use Neomerx\JsonApi\Exceptions\JsonApiException;
17
use ReflectionException;
18
19
/**
20
 * @package Limoncello\Flute
21
 */
22
abstract class FluteSettings implements FluteSettingsInterface
23
{
24
    use ClassIsTrait;
25
26
    /**
27
     * Namespace for string resources.
28
     */
29
    const GENERIC_NAMESPACE = Messages::RESOURCES_NAMESPACE;
30
31
    /**
32
     * Namespace for string resources.
33
     */
34
    public const VALIDATION_NAMESPACE = 'Limoncello.Flute.Validation';
35
36
    /**
37
     * Default page size.
38
     */
39
    public const DEFAULT_PAGE_SIZE = 10;
40
41
    /**
42
     * Default page size.
43
     */
44
    public const DEFAULT_MAX_PAGE_SIZE = 30;
45
46
    /** Serialized validation data index */
47
    protected const JSON_API_DATA_VALIDATION_RULES_SERIALIZED = 0;
48
49
    /** Serialized validation data index */
50
    protected const JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED = self::JSON_API_DATA_VALIDATION_RULES_SERIALIZED + 1;
51
52
    /**
53
     * @param array $appConfig
54
     *
55
     * @return array
56
     *
57
     * @throws ReflectionException
58
     */
59 30
    final public function get(array $appConfig): array
60
    {
61 30
        $defaults = $this->getSettings();
62
63 30
        $defaults[static::KEY_ROUTES_FOLDER]          = $appConfig[A::KEY_ROUTES_FOLDER];
64 30
        $defaults[static::KEY_WEB_CONTROLLERS_FOLDER] = $appConfig[A::KEY_WEB_CONTROLLERS_FOLDER];
65
66 30
        $apiFolder            = $defaults[static::KEY_API_FOLDER] ?? null;
67 30
        $valRulesFolder       = $defaults[static::KEY_JSON_VALIDATION_RULES_FOLDER] ?? null;
68 30
        $jsonCtrlFolder       = $defaults[static::KEY_JSON_CONTROLLERS_FOLDER] ?? null;
69 30
        $schemasFolder        = $defaults[static::KEY_SCHEMAS_FOLDER] ?? null;
70 30
        $schemasFileMask      = $defaults[static::KEY_SCHEMAS_FILE_MASK] ?? null;
71 30
        $jsonDataValFolder    = $defaults[static::KEY_JSON_VALIDATORS_FOLDER] ?? null;
72 30
        $jsonDataValFileMask  = $defaults[static::KEY_JSON_VALIDATORS_FILE_MASK] ?? null;
73 30
        $formsValFolder       = $defaults[static::KEY_FORM_VALIDATORS_FOLDER] ?? null;
74 30
        $formsValFileMask     = $defaults[static::KEY_FORM_VALIDATORS_FILE_MASK] ?? null;
75 30
        $jsonQueryValFolder   = $defaults[static::KEY_QUERY_VALIDATORS_FOLDER] ?? null;
76 30
        $jsonQueryValFileMask = $defaults[static::KEY_QUERY_VALIDATORS_FILE_MASK] ?? null;
77
78 30
        assert(
79 30
            $apiFolder !== null && empty(glob($apiFolder)) === false,
80 30
            "Invalid API folder `$apiFolder`."
81
        );
82 30
        assert(
83 30
            $valRulesFolder !== null && empty(glob($valRulesFolder)) === false,
84 30
            "Invalid validation rules folder `$valRulesFolder`."
85
        );
86 30
        assert(
87 30
            $jsonCtrlFolder !== null && empty(glob($jsonCtrlFolder)) === false,
88 30
            "Invalid JSON API controllers' folder `$jsonCtrlFolder`."
89
        );
90 30
        assert(
91 30
            $schemasFolder !== null && empty(glob($schemasFolder)) === false,
92 30
            "Invalid Schemas folder `$schemasFolder`."
93
        );
94 30
        assert(empty($schemasFileMask) === false, "Invalid Schemas file mask `$schemasFileMask`.");
95 30
        assert(
96 30
            $jsonDataValFolder !== null && empty(glob($jsonDataValFolder)) === false,
97 30
            "Invalid JSON Validators folder `$jsonDataValFolder`."
98
        );
99 30
        assert(empty($jsonDataValFileMask) === false, "Invalid JSON Validators file mask `$jsonDataValFileMask`.");
100 30
        assert(
101 30
            $formsValFolder !== null && empty(glob($formsValFolder)) === false,
102 30
            "Invalid Forms Validators folder `$formsValFolder`."
103
        );
104 30
        assert(empty($formsValFileMask) === false, "Invalid Forms Validators file mask `$formsValFileMask`.");
105 30
        assert(
106 30
            $jsonQueryValFolder !== null && empty(glob($jsonQueryValFolder)) === false,
107 30
            "Invalid Query Validators folder `$jsonQueryValFolder`."
108
        );
109 30
        assert(empty($jsonQueryValFileMask) === false, "Invalid Query Validators file mask `$jsonQueryValFileMask`.");
110
111 30
        $schemasPath         = $schemasFolder . DIRECTORY_SEPARATOR . $schemasFileMask;
112 30
        $jsonDataValPath     = $jsonDataValFolder . DIRECTORY_SEPARATOR . $jsonDataValFileMask;
113 30
        $formsValidatorsPath = $formsValFolder . DIRECTORY_SEPARATOR . $formsValFileMask;
114 30
        $jsonQueryValPath    = $jsonQueryValFolder . DIRECTORY_SEPARATOR . $jsonQueryValFileMask;
115
116 30
        $requireUniqueTypes = $defaults[static::KEY_SCHEMAS_REQUIRE_UNIQUE_TYPES] ?? true;
117
118 30
        $doNotLogExceptions = $defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST] ?? [];
119 30
        unset($defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST]);
120
121
        return $defaults + [
122 30
                static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST__AS_KEYS => array_flip($doNotLogExceptions),
123
124 30
                static::KEY_MODEL_TO_SCHEMA_MAP => $this->createModelToSchemaMap($schemasPath, $requireUniqueTypes),
125
126 30
                static::KEY_JSON_VALIDATION_RULE_SETS_DATA =>
127 30
                    $this->serializeJsonValidationRules($jsonDataValPath, $jsonQueryValPath),
128
129 30
                static::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA =>
130 30
                    $this->serializeFormValidationRules($formsValidatorsPath),
131
            ];
132
    }
133
134
    /**
135
     * @return array
136
     */
137 30
    protected function getSettings(): array
138
    {
139 30
        $jsonOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES;
140
141
        return [
142 30
            static::KEY_SCHEMAS_REQUIRE_UNIQUE_TYPES              => true,
143 30
            static::KEY_SCHEMAS_FILE_MASK                         => '*.php',
144 30
            static::KEY_JSON_VALIDATORS_FILE_MASK                 => '*.php',
145 30
            static::KEY_FORM_VALIDATORS_FILE_MASK                 => '*.php',
146 30
            static::KEY_QUERY_VALIDATORS_FILE_MASK                => '*.php',
147 30
            static::KEY_THROWABLE_TO_JSON_API_EXCEPTION_CONVERTER => null,
148 30
            static::KEY_HTTP_CODE_FOR_UNEXPECTED_THROWABLE        => 500,
149 30
            static::KEY_DEFAULT_PAGING_SIZE                       => static::DEFAULT_PAGE_SIZE,
150 30
            static::KEY_MAX_PAGING_SIZE                           => static::DEFAULT_MAX_PAGE_SIZE,
151 30
            static::KEY_JSON_ENCODE_OPTIONS                       => $jsonOptions,
152 30
            static::KEY_JSON_ENCODE_DEPTH                         => 512,
153 30
            static::KEY_IS_SHOW_VERSION                           => false,
154 30
            static::KEY_META                                      => null,
155 30
            static::KEY_URI_PREFIX                                => null,
156
157 30
            static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST => [
158
                JsonApiException::class,
159
            ],
160
        ];
161
    }
162
163
    /**
164
     * @param string $schemasPath
165
     * @param bool   $requireUniqueTypes
166
     *
167
     * @return array
168
     *
169
     * @throws ReflectionException
170
     */
171 30
    private function createModelToSchemaMap(string $schemasPath, bool $requireUniqueTypes): array
172
    {
173 30
        $map   = [];
174 30
        $types = [];
175 30
        foreach ($this->selectClasses($schemasPath, SchemaInterface::class) as $schemaClass) {
176 30
            assert(
177 30
                is_string($schemaClass) &&
178 30
                class_exists($schemaClass) &&
179 30
                array_key_exists(SchemaInterface::class, class_implements($schemaClass))
180
            );
181
            /** @var SchemaInterface $schemaClass */
182 30
            $modelClass   = $schemaClass::MODEL;
183 30
            $resourceType = $schemaClass::TYPE;
184
185 30
            assert(is_string($modelClass) === true && empty($modelClass) === false);
186 30
            assert(is_string($resourceType) === true && empty($resourceType) === false);
187
188
            // By default it checks that all Schemas have unique resource types. That's a legit case
189
            // to have multiple Schemas for a same resource type however it's more likely that developer
190
            // just forgot to set a unique one. If you do need multiple Schemas for a resource feel free
191
            // to set to turn off this check.
192 30
            assert(
193 30
                $requireUniqueTypes === false || array_key_exists($resourceType, $types) === false,
194 30
                "Are you sure it's not an error to use resource type `$resourceType` more than once?"
195
            );
196 30
            $types[$resourceType] = true;
197
198 30
            $map[$modelClass] = $schemaClass;
199
        }
200
201 30
        return $map;
202
    }
203
204
    /**
205
     * @param string $rulesPath
206
     * @param string $queriesValPath
207
     *
208
     * @return array
209
     *
210
     * @throws ReflectionException
211
     */
212 30
    private function serializeJsonValidationRules(string $rulesPath, string $queriesValPath): array
213
    {
214
        // JSON API data validation rules
215 30
        $dataSerializer = new JsonApiDataRulesSerializer(new BlockSerializer());
216 30
        foreach ($this->selectClasses($rulesPath, JsonApiDataRulesInterface::class) as $rulesClass) {
217 30
            $dataSerializer->addRulesFromClass($rulesClass);
218
        }
219
220
        // JSON API query validation rules
221 30
        $querySerializer = new JsonApiQueryRulesSerializer(new BlockSerializer());
222
        // Add predefined rules for queries...
223 30
        $querySerializer->addRulesFromClass(DefaultQueryValidationRules::class);
224
        // ... and add user defined ones.
225 30
        foreach ($this->selectClasses($queriesValPath, JsonApiQueryRulesInterface::class) as $rulesClass) {
226 30
            $querySerializer->addRulesFromClass($rulesClass);
227
        }
228
229
        return [
230 30
            static::JSON_API_DATA_VALIDATION_RULES_SERIALIZED    => $dataSerializer->getData(),
231 30
            static::JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED => $querySerializer->getData(),
232
        ];
233
    }
234
235
    /**
236
     * @param string $formsValPath
237
     *
238
     * @return array
239
     *
240
     * @throws ReflectionException
241
     */
242 30
    private function serializeFormValidationRules(string $formsValPath): array
243
    {
244 30
        $serializer = new FormRulesSerializer(new BlockSerializer());
245
246 30
        foreach ($this->selectClasses($formsValPath, FormRulesInterface::class) as $rulesClass) {
247 30
            $serializer->addRulesFromClass($rulesClass);
248
        }
249
250 30
        return $serializer->getData();
251
    }
252
253
    // serialization above makes some assumptions about format of returned data
254
    // the methods below help to deal with the data encapsulation
255
256
    /**
257
     * @param array $fluteSettings
258
     *
259
     * @return array
260
     */
261 12
    public static function getJsonDataSerializedRules(array $fluteSettings): array
262
    {
263 12
        $serializedRulesKey = static::KEY_JSON_VALIDATION_RULE_SETS_DATA;
264 12
        $dataSubKey         = static::JSON_API_DATA_VALIDATION_RULES_SERIALIZED;
265
266 12
        return $fluteSettings[$serializedRulesKey][$dataSubKey];
267
    }
268
269
    /**
270
     * @param array $fluteSettings
271
     *
272
     * @return array
273
     */
274 21
    public static function getJsonQuerySerializedRules(array $fluteSettings): array
275
    {
276 21
        $serializedRulesKey = static::KEY_JSON_VALIDATION_RULE_SETS_DATA;
277 21
        $dataSubKey         = static::JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED;
278
279 21
        return $fluteSettings[$serializedRulesKey][$dataSubKey];
280
    }
281
282
    /**
283
     * @param array $fluteSettings
284
     *
285
     * @return array
286
     */
287 1
    public static function getFormSerializedRules(array $fluteSettings): array
288
    {
289 1
        return $fluteSettings[static::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA];
290
    }
291
}
292