Completed
Push — develop ( 20327f...69b019 )
by Neomerx
10:22 queued 02:22
created

FluteSettings::createJsonValidationRulesSetData()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 4

Importance

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