Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
131 / 131
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Configuration
100.00% covered (success)
100.00%
131 / 131
100.00% covered (success)
100.00%
2 / 2
9
100.00% covered (success)
100.00%
1 / 1
 addCmsOptions
n/a
0 / 0
n/a
0 / 0
0
 addApiOptions
n/a
0 / 0
n/a
0 / 0
0
 getConfigTreeBuilder
100.00% covered (success)
100.00%
125 / 125
100.00% covered (success)
100.00%
1 / 1
6
 getDefaultLanguageTypehint
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2namespace Apie\Common\Config;
3
4use Apie\Common\ValueObjects\EntityNamespace;
5use Apie\DoctrineEntityDatalayer\IndexStrategy\DirectIndexStrategy;
6use Apie\IanaValueObjects\LanguageAndRegion;
7use PrinsFrank\Standards\Language\LanguageAlpha2;
8use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
9use Symfony\Component\Config\Definition\Builder\TreeBuilder;
10use Symfony\Component\Config\Definition\ConfigurationInterface;
11
12/**
13 * Configuration
14 */
15abstract class Configuration implements ConfigurationInterface
16{
17    private const ENABLE_CONFIGS = [
18        'enable_basic_auth' => 'Apie\Common\BasicAuth\AddBasicAuthContextBuilder',
19        'enable_ai_instructor' => 'Apie\AiInstructor\AiInstructor',
20        'enable_common_plugin' => 'Apie\ApieCommonPlugin\ApieCommonPlugin',
21        'enable_cms' => 'Apie\Cms\RouteDefinitions\CmsRouteDefinitionProvider',
22        'enable_cms_dropdown' => 'Apie\CmsApiDropdownOption\RouteDefinitions\DropdownOptionsForExistingObjectRouteDefinition',
23        'enable_doctrine_entity_converter' => 'Apie\DoctrineEntityConverter\OrmBuilder',
24        'enable_doctrine_entity_datalayer' => 'Apie\DoctrineEntityDatalayer\DoctrineEntityDatalayer',
25        'enable_export' => 'Apie\Export\EntityExport',
26        'enable_faker' => 'Apie\Faker\ApieObjectFaker',
27        'enable_ftp' => 'Apie\FtpServer\FtpServerCommand',
28        'enable_graphql' => 'Apie\Graphql\Factories\GraphqlSchemaFactory',
29        'enable_maker' => 'Apie\Maker\Utils',
30        'enable_mcp_server' => 'Apie\McpServer\RunMcpServerCommand',
31        'enable_rest_api' => 'Apie\RestApi\OpenApi\OpenApiGenerator',
32        'enable_console' => 'Apie\Console\ConsoleCommandFactory',
33        'enable_twig_template_layout_renderer' => 'Apie\TwigTemplateLayoutRenderer\TwigRenderer',
34        'enable_typescript_client_builder' => 'Apie\TypescriptClientBuilder\RouteDefinitions\CodeRouteDefinitionProvider',
35        'enable_webdav' => 'Apie\Webdav\Controller\WebdavController',
36    ];
37
38    abstract protected function addCmsOptions(ArrayNodeDefinition $arrayNode): void;
39
40    abstract protected function addApiOptions(ArrayNodeDefinition $arrayNode): void;
41
42    public function getConfigTreeBuilder(): TreeBuilder
43    {
44        $treeBuilder = new TreeBuilder('apie');
45
46        $children = $treeBuilder->getRootNode()->children();
47        $children->booleanNode('enable_core')->defaultValue(true)->end();
48        $children->scalarNode('encryption_key')->end();
49        $cmsConfig = $children->arrayNode('cms');
50        $cmsConfig->children()
51          ->scalarNode('base_url')->defaultValue('/cms')->end()
52          ->arrayNode('asset_folders')->scalarPrototype()->end()
53        ->end();
54        $this->addCmsOptions($cmsConfig);
55        $apiConfig = $children->arrayNode('rest_api');
56        $apiConfig->children()
57          ->scalarNode('base_url')->defaultValue('/api')->end()
58        ->end();
59        $this->addApiOptions($apiConfig);
60
61        $openApiConfig = $children->arrayNode('open_api');
62        $openApiConfig->children()
63            ->integerNode('max_enum_size')->defaultValue(100)->end()
64        ->end();
65
66        $ftpConfig = $children->arrayNode('ftp_server');
67        $ftpConfig->children()
68          ->scalarNode('public_ip')->defaultValue('127.0.0.1')->end()
69          ->scalarNode('passive_min_port')->defaultValue('49152')->end()
70          ->scalarNode('passive_max_port')->defaultValue('65534')->end()
71        ->end();
72
73        $ftpConfig = $children->arrayNode('graphql');
74        $ftpConfig->children()
75          ->scalarNode('base_url')->defaultValue('graphql')->end()
76        ->end();
77
78        $aiConfig = $children->arrayNode('ai')
79          ->addDefaultsIfNotSet();
80        $aiConfig->children()
81            ->scalarNode('base_url')->defaultValue('https://api.openai.com/v1')->end()
82            ->scalarNode('api_key')->defaultValue('no-value')->end()
83        ->end();
84
85        $children->arrayNode('datalayers')
86                ->children()
87                    ->scalarNode('default_datalayer')->isRequired()->end()
88                    ->arrayNode('context_mapping')
89                        ->useAttributeAsKey('name')
90                        ->arrayPrototype()
91                            ->isRequired()
92                            ->children()
93                                ->scalarNode('default_datalayer')->isRequired()->end()
94                                ->arrayNode('entity_mapping')
95                                    ->useAttributeAsKey('class')
96                                    ->scalarPrototype()
97                                    ->end()
98                                ->end()
99                            ->end()
100                        ->end()
101                    ->end()
102                ->end()
103            ->end()
104            ->arrayNode('doctrine')
105                ->children()
106                    ->arrayNode('indexing')
107                        ->children()
108                            ->enumNode('type')->values(['direct', 'late', 'background', 'custom'])->defaultValue('direct')->end()
109                            ->scalarNode('service')->defaultValue(DirectIndexStrategy::class)->end()
110                        ->end()
111                    ->end()
112                    ->scalarNode('build_once')->defaultValue(false)->end()
113                    ->scalarNode('run_migrations')->defaultValue(true)->end()
114                    ->arrayNode('connection_params')
115                      ->defaultValue(['driver' => 'pdo_sqlite'])
116                      ->useAttributeAsKey('class')
117                      ->scalarPrototype()
118                      ->end()
119                    ->end()
120                ->end()
121            ->end()
122            ->arrayNode('storage')
123              ->arrayPrototype()
124                ->children()
125                  ->scalarNode('class')->isRequired()->end()
126                  ->arrayNode('options')->defaultValue([])
127                    ->scalarPrototype()
128                    ->end()
129                  ->end()
130                ->end()
131              ->end()
132            ->end()
133            ->arrayNode('maker')
134                ->children()
135                  ->scalarNode('target_path')->defaultValue(false)->end()
136                  ->scalarNode('target_namespace')->defaultValue('App\Apie')->end()
137                ->end()
138            ->end()
139            ->scalarNode('language_typehint')->defaultValue(self::getDefaultLanguageTypehint())->end()
140            ->arrayNode('bounded_contexts')
141                ->useAttributeAsKey('name')
142                ->arrayPrototype()
143                ->beforeNormalization()
144                    ->always(
145                        function (array $v) {
146                            if (empty($v['policies_folder']) && !empty($v['entities_folder'])) {
147                                $v['policies_folder'] = rtrim($v['entities_folder'], '/\\') . '/../Policies';
148                            }
149
150                            if (empty($v['policies_namespace']) && !empty($v['entities_namespace'])) {
151                                $entityNamespace = EntityNamespace::fromNative($v['entities_namespace']);
152                                $v['policies_namespace'] = $entityNamespace->getParentNamespace()->getChildNamespace('Policies')->toNative();
153                            }
154
155                            return $v;
156                        }
157                    )->end()
158                    ->children()
159                        ->scalarNode('entities_folder')->isRequired()->end()
160                        ->scalarNode('entities_namespace')->isRequired()->end()
161                        ->scalarNode('actions_folder')->isRequired()->end()
162                        ->scalarNode('actions_namespace')->isRequired()->end()
163                        ->scalarNode('policies_folder')->defaultNull()->end()
164                        ->scalarNode('policies_namespace')->defaultNull()->end()
165                    ->end()
166                ->end()
167            ->end()
168            ->arrayNode('scan_bounded_contexts')
169                ->children()
170                    ->scalarNode('search_path')->end()
171                    ->scalarNode('search_namespace')->end()
172                ->end()
173            ->end();
174        $childNode = $treeBuilder->getRootNode()->children();
175        foreach (self::ENABLE_CONFIGS as $configKey => $classNameToExist) {
176            $childNode->booleanNode($configKey)->defaultValue(class_exists($classNameToExist));
177        }
178        $childNode->scalarNode('remote_mcp_path')->defaultValue(null)->end();
179        return $treeBuilder;
180    }
181
182    public static function getDefaultLanguageTypehint(): ?string
183    {
184        $languageTypehint = null;
185        if (class_exists(LanguageAlpha2::class)) {
186            $languageTypehint = LanguageAlpha2::class;
187        }
188        if (class_exists(LanguageAndRegion::class)) {
189            $languageTypehint = LanguageAndRegion::class;
190        }
191        return $languageTypehint;
192    }
193}