Completed
Push — develop ( 10c295...73136c )
by Neomerx
03:55
created

createValidationAttributeRulesSetData()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 18
cts 18
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 18
nc 4
nop 2
crap 7
1
<?php namespace Limoncello\Flute\Package;
2
3
use Generator;
4
use Limoncello\Contracts\Settings\SettingsInterface;
5
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
6
use Limoncello\Flute\Contracts\Validation\FormRuleSetInterface;
7
use Limoncello\Flute\Contracts\Validation\JsonApiRuleSetInterface;
8
use Limoncello\Flute\Contracts\Validation\QueryRuleSetInterface;
9
use Limoncello\Flute\Validation\Form\Execution\AttributeRulesSerializer;
10
use Limoncello\Flute\Validation\JsonApi\Execution\JsonApiRuleSerializer;
11
use Neomerx\JsonApi\Exceptions\JsonApiException;
12
13
/**
14
 * @package Limoncello\Flute
15
 */
16
abstract class FluteSettings implements SettingsInterface
17
{
18
    /**
19
     * Namespace for string resources.
20
     */
21
    const VALIDATION_NAMESPACE = 'Limoncello.Flute.Validation';
22
23
    /**
24
     * @param string $path
25
     * @param string $implementClassName
26
     *
27
     * @return Generator
28
     */
29
    abstract protected function selectClasses(string $path, string $implementClassName): Generator;
30
31
    /** Config key */
32
    const KEY_DO_NOT_LOG_EXCEPTIONS_LIST = 0;
33
34
    /** Config key */
35
    const KEY_DO_NOT_LOG_EXCEPTIONS_LIST__AS_KEYS = self::KEY_DO_NOT_LOG_EXCEPTIONS_LIST + 1;
36
37
    /** Config key
38
     *
39
     * By default it checks that all Schemes have unique resource types. That's a legit case
40
     * to have multiple Schemes for a same resource type however it's more likely that developer
41
     * just forgot to set a unique one. If you do need multiple Schemes for a resource feel free
42
     * to set it to `false`.
43
     *
44
     * Default: true
45
     */
46
    const KEY_SCHEMES_REQUIRE_UNIQUE_TYPES = self::KEY_DO_NOT_LOG_EXCEPTIONS_LIST__AS_KEYS + 1;
47
48
    /** Config key */
49
    const KEY_SCHEMES_FOLDER = self::KEY_SCHEMES_REQUIRE_UNIQUE_TYPES + 1;
50
51
    /** Config key */
52
    const KEY_SCHEMES_FILE_MASK = self::KEY_SCHEMES_FOLDER + 1;
53
54
    /** Config key */
55
    const KEY_JSON_VALIDATORS_FOLDER = self::KEY_SCHEMES_FILE_MASK + 1;
56
57
    /** Config key */
58
    const KEY_JSON_VALIDATORS_FILE_MASK = self::KEY_JSON_VALIDATORS_FOLDER + 1;
59
60
    /** Config key */
61
    const KEY_FORM_VALIDATORS_FOLDER = self::KEY_JSON_VALIDATORS_FILE_MASK + 1;
62
63
    /** Config key */
64
    const KEY_FORM_VALIDATORS_FILE_MASK = self::KEY_FORM_VALIDATORS_FOLDER + 1;
65
66
    /** Config key */
67
    const KEY_QUERY_VALIDATORS_FOLDER = self::KEY_FORM_VALIDATORS_FILE_MASK + 1;
68
69
    /** Config key */
70
    const KEY_QUERY_VALIDATORS_FILE_MASK = self::KEY_QUERY_VALIDATORS_FOLDER + 1;
71
72
    /** Config key */
73
    const KEY_HTTP_CODE_FOR_UNEXPECTED_THROWABLE = self::KEY_QUERY_VALIDATORS_FILE_MASK + 1;
74
75
    /** Config key */
76
    const KEY_THROWABLE_TO_JSON_API_EXCEPTION_CONVERTER = self::KEY_HTTP_CODE_FOR_UNEXPECTED_THROWABLE + 1;
77
78
    /** Config key */
79
    const KEY_MODEL_TO_SCHEME_MAP = self::KEY_THROWABLE_TO_JSON_API_EXCEPTION_CONVERTER + 1;
80
81
    /** Config key */
82
    const KEY_JSON_VALIDATION_RULE_SETS_DATA = self::KEY_MODEL_TO_SCHEME_MAP + 1;
83
84
    /** Config key */
85
    const KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA = self::KEY_JSON_VALIDATION_RULE_SETS_DATA + 1;
86
87
    /** Config key */
88
    const KEY_DEFAULT_PAGING_SIZE = self::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA + 1;
89
90
    /** Config key */
91
    const KEY_MAX_PAGING_SIZE = self::KEY_DEFAULT_PAGING_SIZE + 1;
92
93
    /** Config key */
94
    const KEY_JSON_ENCODE_OPTIONS = self::KEY_MAX_PAGING_SIZE + 1;
95
96
    /** Config key */
97
    const KEY_JSON_ENCODE_DEPTH = self::KEY_JSON_ENCODE_OPTIONS + 1;
98
99
    /** Config key */
100
    const KEY_IS_SHOW_VERSION = self::KEY_JSON_ENCODE_DEPTH + 1;
101
102
    /** Config key */
103
    const KEY_META = self::KEY_IS_SHOW_VERSION + 1;
104
105
    /** Config key */
106
    const KEY_URI_PREFIX = self::KEY_META + 1;
107
108
    /** Config key */
109
    protected const KEY_LAST = self::KEY_URI_PREFIX + 1;
110
111
    /**
112
     * @param array $appConfig
113
     *
114
     * @return array
115
     */
116 28
    final public function get(array $appConfig): array
117
    {
118 28
        $defaults = $this->getSettings();
119
120 28
        $schemesFolder    = $defaults[static::KEY_SCHEMES_FOLDER] ?? null;
121 28
        $schemesFileMask  = $defaults[static::KEY_SCHEMES_FILE_MASK] ?? null;
122 28
        $jsonValFolder    = $defaults[static::KEY_JSON_VALIDATORS_FOLDER] ?? null;
123 28
        $jsonValFileMask  = $defaults[static::KEY_JSON_VALIDATORS_FILE_MASK] ?? null;
124 28
        $formsValFolder   = $defaults[static::KEY_FORM_VALIDATORS_FOLDER] ?? null;
125 28
        $formsValFileMask = $defaults[static::KEY_FORM_VALIDATORS_FILE_MASK] ?? null;
126 28
        $queryValFolder   = $defaults[static::KEY_QUERY_VALIDATORS_FOLDER] ?? null;
127 28
        $queryValFileMask = $defaults[static::KEY_QUERY_VALIDATORS_FILE_MASK] ?? null;
128
129 28
        assert(
130 28
            $schemesFolder !== null && empty(glob($schemesFolder)) === false,
131 28
            "Invalid Schemes folder `$schemesFolder`."
132
        );
133 28
        assert(empty($schemesFileMask) === false, "Invalid Schemes file mask `$schemesFileMask`.");
134 28
        assert(
135 28
            $jsonValFolder !== null && empty(glob($jsonValFolder)) === false,
136 28
            "Invalid JSON Validators folder `$jsonValFolder`."
137
        );
138 28
        assert(empty($jsonValFileMask) === false, "Invalid JSON Validators file mask `$jsonValFileMask`.");
139 28
        assert(
140 28
            $formsValFolder !== null && empty(glob($formsValFolder)) === false,
141 28
            "Invalid Forms Validators folder `$formsValFolder`."
142
        );
143 28
        assert(empty($formsValFileMask) === false, "Invalid Forms Validators file mask `$formsValFileMask`.");
144 28
        assert(
145 28
            $queryValFolder !== null && empty(glob($queryValFolder)) === false,
146 28
            "Invalid Query Validators folder `$queryValFolder`."
147
        );
148 28
        assert(empty($queryValFileMask) === false, "Invalid Query Validators file mask `$queryValFileMask`.");
149
150 28
        $schemesPath         = $schemesFolder . DIRECTORY_SEPARATOR . $schemesFileMask;
151 28
        $jsonValidatorsPath  = $jsonValFolder . DIRECTORY_SEPARATOR . $jsonValFileMask;
152 28
        $formsValidatorsPath = $formsValFolder . DIRECTORY_SEPARATOR . $formsValFileMask;
153 28
        $queryValidatorsPath = $queryValFolder . DIRECTORY_SEPARATOR . $queryValFileMask;
154
155 28
        $requireUniqueTypes = $defaults[static::KEY_SCHEMES_REQUIRE_UNIQUE_TYPES] ?? true;
156
157 28
        $doNotLogExceptions = $defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST] ?? [];
158 28
        unset($defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST]);
159
160
        return $defaults + [
161 28
                static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST__AS_KEYS => array_flip($doNotLogExceptions),
162
163 28
                static::KEY_MODEL_TO_SCHEME_MAP => $this->createModelToSchemeMap($schemesPath, $requireUniqueTypes),
164
165 28
                static::KEY_JSON_VALIDATION_RULE_SETS_DATA =>
166 28
                    $this->createJsonValidationRulesSetData($jsonValidatorsPath),
167
168 28
                static::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA =>
169 28
                    $this->createValidationAttributeRulesSetData($formsValidatorsPath, $queryValidatorsPath),
170
            ];
171
    }
172
173
    /**
174
     * @return array
175
     */
176 28
    protected function getSettings(): array
177
    {
178 28
        $jsonOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES;
179
180
        return [
181 28
            static::KEY_SCHEMES_REQUIRE_UNIQUE_TYPES              => true,
182 28
            static::KEY_SCHEMES_FILE_MASK                         => '*.php',
183 28
            static::KEY_JSON_VALIDATORS_FILE_MASK                 => '*.php',
184 28
            static::KEY_FORM_VALIDATORS_FILE_MASK                 => '*.php',
185 28
            static::KEY_QUERY_VALIDATORS_FILE_MASK                => '*.php',
186 28
            static::KEY_THROWABLE_TO_JSON_API_EXCEPTION_CONVERTER => null,
187 28
            static::KEY_HTTP_CODE_FOR_UNEXPECTED_THROWABLE        => 500,
188 28
            static::KEY_DEFAULT_PAGING_SIZE                       => 20,
189 28
            static::KEY_MAX_PAGING_SIZE                           => 100,
190 28
            static::KEY_JSON_ENCODE_OPTIONS                       => $jsonOptions,
191 28
            static::KEY_JSON_ENCODE_DEPTH                         => 512,
192 28
            static::KEY_IS_SHOW_VERSION                           => false,
193 28
            static::KEY_META                                      => null,
194 28
            static::KEY_URI_PREFIX                                => null,
195
196 28
            static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST => [
197
                JsonApiException::class,
198
            ],
199
        ];
200
    }
201
202
    /**
203
     * @param string $schemesPath
204
     * @param bool   $requireUniqueTypes
205
     *
206
     * @return array
207
     */
208 28
    private function createModelToSchemeMap(string $schemesPath, bool $requireUniqueTypes): array
209
    {
210 28
        $map   = [];
211 28
        $types = [];
212 28
        foreach ($this->selectClasses($schemesPath, SchemaInterface::class) as $schemeClass) {
213 28
            assert(
214 28
                is_string($schemeClass) &&
215 28
                class_exists($schemeClass) &&
216 28
                array_key_exists(SchemaInterface::class, class_implements($schemeClass))
217
            );
218
            /** @var SchemaInterface $schemeClass */
219 28
            $modelClass   = $schemeClass::MODEL;
220 28
            $resourceType = $schemeClass::TYPE;
221
222 28
            assert(is_string($modelClass) === true && empty($modelClass) === false);
223 28
            assert(is_string($resourceType) === true && empty($resourceType) === false);
224
225
            // By default it checks that all Schemes have unique resource types. That's a legit case
226
            // to have multiple Schemes for a same resource type however it's more likely that developer
227
            // just forgot to set a unique one. If you do need multiple Schemes for a resource feel free
228
            // to set to turn off this check.
229 28
            assert(
230 28
                $requireUniqueTypes === false || array_key_exists($resourceType, $types) === false,
231 28
                "Are you sure it's not an error to use resource type `$resourceType` more than once?"
232
            );
233 28
            $types[$resourceType] = true;
234
235 28
            $map[$modelClass] = $schemeClass;
236
        }
237
238 28
        return $map;
239
    }
240
241
    /**
242
     * @param string $validatorsPath
243
     *
244
     * @return array
245
     */
246 28
    private function createJsonValidationRulesSetData(string $validatorsPath): array
247
    {
248 28
        $serializer = new JsonApiRuleSerializer();
249 28
        foreach ($this->selectClasses($validatorsPath, JsonApiRuleSetInterface::class) as $setClass) {
250
            /** @var string $setName */
251 28
            $setName = $setClass;
252 28
            assert(
253 28
                is_string($setClass) &&
254 28
                class_exists($setClass) &&
255 28
                array_key_exists(JsonApiRuleSetInterface::class, class_implements($setClass))
256
            );
257
            /** @var JsonApiRuleSetInterface $setClass */
258 28
            $serializer->addResourceRules(
259 28
                $setName,
260 28
                $setClass::getIdRule(),
261 28
                $setClass::getTypeRule(),
262 28
                $setClass::getAttributeRules(),
263 28
                $setClass::getToOneRelationshipRules(),
264 28
                $setClass::getToManyRelationshipRules()
265
            );
266
        }
267
268 28
        $ruleSetsData = $serializer->getData();
269
270 28
        return $ruleSetsData;
271
    }
272
273
    /**
274
     * @param string $formsValPath
275
     * @param string $queriesValPath
276
     *
277
     * @return array
278
     */
279 28
    private function createValidationAttributeRulesSetData(string $formsValPath, string $queriesValPath): array
280
    {
281 28
        $serializer = new AttributeRulesSerializer();
282 28
        foreach ($this->selectClasses($formsValPath, FormRuleSetInterface::class) as $setClass) {
283
            /** @var string $setName */
284 28
            $setName = $setClass;
285 28
            assert(
286 28
                is_string($setClass) &&
287 28
                class_exists($setClass) &&
288 28
                array_key_exists(FormRuleSetInterface::class, class_implements($setClass))
289
            );
290
            /** @var FormRuleSetInterface $setClass */
291 28
            $serializer->addResourceRules($setName, $setClass::getAttributeRules());
292
        }
293 28
        foreach ($this->selectClasses($queriesValPath, QueryRuleSetInterface::class) as $setClass) {
294
            /** @var string $setName */
295 28
            $setName = $setClass;
296 28
            assert(
297 28
                is_string($setClass) &&
298 28
                class_exists($setClass) &&
299 28
                array_key_exists(QueryRuleSetInterface::class, class_implements($setClass))
300
            );
301
            /** @var QueryRuleSetInterface $setClass */
302 28
            $serializer->addResourceRules($setName, $setClass::getAttributeRules());
303
        }
304
305 28
        $ruleSetsData = $serializer->getData();
306
307 28
        return $ruleSetsData;
308
    }
309
}
310