Completed
Push — master ( b6f561...db9bd8 )
by Neomerx
03:16
created

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