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_exchangerate' => 'Apie\ExchangeRate\ExchangeRateService',
29        'enable_graphql' => 'Apie\Graphql\Factories\GraphqlSchemaFactory',
30        'enable_maker' => 'Apie\Maker\Utils',
31        'enable_mcp_server' => 'Apie\McpServer\RunMcpServerCommand',
32        'enable_rest_api' => 'Apie\RestApi\OpenApi\OpenApiGenerator',
33        'enable_console' => 'Apie\Console\ConsoleCommandFactory',
34        'enable_twig_template_layout_renderer' => 'Apie\TwigTemplateLayoutRenderer\TwigRenderer',
35        'enable_typescript_client_builder' => 'Apie\TypescriptClientBuilder\RouteDefinitions\CodeRouteDefinitionProvider',
36        'enable_webdav' => 'Apie\Webdav\Controller\WebdavController',
37    ];
38
39    abstract protected function addCmsOptions(ArrayNodeDefinition $arrayNode): void;
40
41    abstract protected function addApiOptions(ArrayNodeDefinition $arrayNode): void;
42
43    public function getConfigTreeBuilder(): TreeBuilder
44    {
45        $treeBuilder = new TreeBuilder('apie');
46
47        $children = $treeBuilder->getRootNode()->children();
48        $children->booleanNode('enable_core')->defaultValue(true)->end();
49        $children->scalarNode('encryption_key')->end();
50        $cmsConfig = $children->arrayNode('cms');
51        $cmsConfig->children()
52          ->scalarNode('base_url')->defaultValue('/cms')->end()
53          ->arrayNode('asset_folders')->scalarPrototype()->end()
54        ->end();
55        $this->addCmsOptions($cmsConfig);
56        $apiConfig = $children->arrayNode('rest_api');
57        $apiConfig->children()
58          ->scalarNode('base_url')->defaultValue('/api')->end()
59        ->end();
60        $this->addApiOptions($apiConfig);
61
62        $openApiConfig = $children->arrayNode('open_api');
63        $openApiConfig->children()
64            ->integerNode('max_enum_size')->defaultValue(100)->end()
65        ->end();
66
67        $ftpConfig = $children->arrayNode('ftp_server');
68        $ftpConfig->children()
69          ->scalarNode('public_ip')->defaultValue('127.0.0.1')->end()
70          ->scalarNode('passive_min_port')->defaultValue('49152')->end()
71          ->scalarNode('passive_max_port')->defaultValue('65534')->end()
72        ->end();
73
74        $ftpConfig = $children->arrayNode('graphql');
75        $ftpConfig->children()
76          ->scalarNode('base_url')->defaultValue('graphql')->end()
77        ->end();
78
79        $aiConfig = $children->arrayNode('ai')
80          ->addDefaultsIfNotSet();
81        $aiConfig->children()
82            ->scalarNode('base_url')->defaultValue('https://api.openai.com/v1')->end()
83            ->scalarNode('api_key')->defaultValue('no-value')->end()
84        ->end();
85
86        $children->arrayNode('datalayers')
87                ->children()
88                    ->scalarNode('default_datalayer')->isRequired()->end()
89                    ->arrayNode('context_mapping')
90                        ->useAttributeAsKey('name')
91                        ->arrayPrototype()
92                            ->isRequired()
93                            ->children()
94                                ->scalarNode('default_datalayer')->isRequired()->end()
95                                ->arrayNode('entity_mapping')
96                                    ->useAttributeAsKey('class')
97                                    ->scalarPrototype()
98                                    ->end()
99                                ->end()
100                            ->end()
101                        ->end()
102                    ->end()
103                ->end()
104            ->end()
105            ->arrayNode('doctrine')
106                ->children()
107                    ->arrayNode('indexing')
108                        ->children()
109                            ->enumNode('type')->values(['direct', 'late', 'background', 'custom'])->defaultValue('direct')->end()
110                            ->scalarNode('service')->defaultValue(DirectIndexStrategy::class)->end()
111                        ->end()
112                    ->end()
113                    ->scalarNode('build_once')->defaultValue(false)->end()
114                    ->scalarNode('run_migrations')->defaultValue(true)->end()
115                    ->arrayNode('connection_params')
116                      ->defaultValue(['driver' => 'pdo_sqlite'])
117                      ->useAttributeAsKey('class')
118                      ->scalarPrototype()
119                      ->end()
120                    ->end()
121                ->end()
122            ->end()
123            ->arrayNode('storage')
124              ->arrayPrototype()
125                ->children()
126                  ->scalarNode('class')->isRequired()->end()
127                  ->arrayNode('options')->defaultValue([])
128                    ->scalarPrototype()
129                    ->end()
130                  ->end()
131                ->end()
132              ->end()
133            ->end()
134            ->arrayNode('maker')
135                ->children()
136                  ->scalarNode('target_path')->defaultValue(false)->end()
137                  ->scalarNode('target_namespace')->defaultValue('App\Apie')->end()
138                ->end()
139            ->end()
140            ->scalarNode('language_typehint')->defaultValue(self::getDefaultLanguageTypehint())->end()
141            ->arrayNode('bounded_contexts')
142                ->useAttributeAsKey('name')
143                ->arrayPrototype()
144                ->beforeNormalization()
145                    ->always(
146                        function (array $v) {
147                            if (empty($v['policies_folder']) && !empty($v['entities_folder'])) {
148                                $v['policies_folder'] = rtrim($v['entities_folder'], '/\\') . '/../Policies';
149                            }
150
151                            if (empty($v['policies_namespace']) && !empty($v['entities_namespace'])) {
152                                $entityNamespace = EntityNamespace::fromNative($v['entities_namespace']);
153                                $v['policies_namespace'] = $entityNamespace->getParentNamespace()->getChildNamespace('Policies')->toNative();
154                            }
155
156                            return $v;
157                        }
158                    )->end()
159                    ->children()
160                        ->scalarNode('entities_folder')->isRequired()->end()
161                        ->scalarNode('entities_namespace')->isRequired()->end()
162                        ->scalarNode('actions_folder')->isRequired()->end()
163                        ->scalarNode('actions_namespace')->isRequired()->end()
164                        ->scalarNode('policies_folder')->defaultNull()->end()
165                        ->scalarNode('policies_namespace')->defaultNull()->end()
166                    ->end()
167                ->end()
168            ->end()
169            ->arrayNode('scan_bounded_contexts')
170                ->children()
171                    ->scalarNode('search_path')->end()
172                    ->scalarNode('search_namespace')->end()
173                ->end()
174            ->end();
175        $childNode = $treeBuilder->getRootNode()->children();
176        foreach (self::ENABLE_CONFIGS as $configKey => $classNameToExist) {
177            $childNode->booleanNode($configKey)->defaultValue(class_exists($classNameToExist));
178        }
179        $childNode->scalarNode('remote_mcp_path')->defaultValue(null)->end();
180        return $treeBuilder;
181    }
182
183    public static function getDefaultLanguageTypehint(): ?string
184    {
185        $languageTypehint = null;
186        if (class_exists(LanguageAlpha2::class)) {
187            $languageTypehint = LanguageAlpha2::class;
188        }
189        if (class_exists(LanguageAndRegion::class)) {
190            $languageTypehint = LanguageAndRegion::class;
191        }
192        return $languageTypehint;
193    }
194}