ApiGenerator   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 50
c 1
b 0
f 0
dl 0
loc 132
ccs 0
cts 37
cp 0
rs 10
wmc 11

7 Methods

Rating   Name   Duplication   Size   Complexity  
A generate() 0 10 2
A updateConfiguration() 0 24 1
A generateFixtures() 0 10 1
A __construct() 0 10 1
A generateFromSingleSchema() 0 7 2
A resolveTemplateSchema() 0 11 3
A updateDocumentation() 0 8 1
1
<?php
2
/**
3
 * Copyright (c) 2020.
4
 * @author Paweł Antosiak <[email protected]>
5
 */
6
7
declare(strict_types=1);
8
9
namespace Gorynych\Generator;
10
11
use Cake\Collection\Collection;
12
use Gorynych\Adapter\TwigAdapter;
13
use Gorynych\Resource\AbstractResource;
14
use Gorynych\Resource\CollectionResourceInterface;
15
use Gorynych\Resource\ResourceRegistryBuilder;
16
use Gorynych\Resource\ResourceInterface;
17
use Gorynych\Testing\EntityMock;
18
use Gorynych\Util\EnvAccess;
19
use Gorynych\Util\OAReader;
20
use Symfony\Component\Yaml\Yaml;
21
22
final class ApiGenerator
23
{
24
    private TwigAdapter $templateEngine;
25
    private ResourceRegistryBuilder $resourcesConfigBuilder;
26
    private FileWriter $fileWriter;
27
    private OAReader $oaReader;
28
29
    /** @var \ReflectionClass<AbstractResource>|null */
30
    private ?\ReflectionClass $resourceReflection;
31
    private ?TemplateDto $templateDto;
32
33
    public function __construct(
34
        TwigAdapter $templateEngine,
35
        ResourceRegistryBuilder $resourcesConfigBuilder,
36
        FileWriter $fileWriter,
37
        OAReader $oaReader
38
    ) {
39
        $this->templateEngine = $templateEngine;
40
        $this->resourcesConfigBuilder = $resourcesConfigBuilder;
41
        $this->fileWriter = $fileWriter;
42
        $this->oaReader = $oaReader;
43
    }
44
45
    /**
46
     * @param \ReflectionClass<AbstractResource> $resourceReflection
47
     */
48
    public function generate(\ReflectionClass $resourceReflection): void
49
    {
50
        $this->resourceReflection = $resourceReflection;
51
        $this->templateDto = TemplateDto::fromReflection($this->resourceReflection);
52
53
        foreach ($this->resolveTemplateSchema() as $schema) {
54
            $this->generateFromSingleSchema($schema);
55
        }
56
57
        $this->generateFixtures()->updateConfiguration()->updateDocumentation();
58
    }
59
60
    /**
61
     * Generates all items specified by given schema
62
     * That includes resource operation and its corresponding test case
63
     *
64
     * @param string[][] $schema
65
     */
66
    private function generateFromSingleSchema(array $schema): void
67
    {
68
        foreach ($schema as $itemName => $item) {
69
            $path = sprintf(EnvAccess::get('PROJECT_DIR') . $item['output'], $this->templateDto->entityClassName);
70
            $content = $this->templateEngine->render($item['template'], (array)$this->templateDto);
71
72
            $this->fileWriter->write($path, $content);
73
        }
74
    }
75
76
    /**
77
     * Generates test fixtures
78
     *
79
     * @return self
80
     */
81
    private function generateFixtures(): self
82
    {
83
        $path = EnvAccess::get('PROJECT_DIR') . "/config/fixtures/{$this->templateDto->resourceSimpleName}.yaml";
84
        $entityNamespace = $this->templateDto->entityNamespace;
85
86
        $fixtures[$entityNamespace]["fixture_1"] = (array) EntityMock::create($entityNamespace);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$fixtures was never initialized. Although not strictly required by PHP, it is generally a good practice to add $fixtures = array(); before regardless.
Loading history...
87
88
        $this->fileWriter->write($path, Yaml::dump($fixtures, 3, 2));
89
90
        return $this;
91
    }
92
93
    /**
94
     * Updates resources.yaml configuration file
95
     *
96
     * @return self
97
     */
98
    private function updateConfiguration(): self
99
    {
100
        $templateParameters = $this->templateDto;
101
102
        $newConfigRecords = (new Collection($this->resolveTemplateSchema()))
103
            ->map(static function (array $operationSchema) use ($templateParameters): string {
104
                return $templateParameters->rootNamespace . '\\' .
105
                    str_replace(
106
                        '/',
107
                        '\\',
108
                        sprintf(
109
                            rtrim(ltrim($operationSchema['operation']['output'], '/src'), '.php'),
110
                            $templateParameters->entityClassName
111
                        )
112
                    );
113
            })
114
            ->toList();
115
116
        $this->resourcesConfigBuilder
117
            ->selectResource($this->resourceReflection->getName())
0 ignored issues
show
Bug introduced by
The method getName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

117
            ->selectResource($this->resourceReflection->/** @scrutinizer ignore-call */ getName())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
            ->mergeOperations(...$newConfigRecords)
119
            ->save();
120
121
        return $this;
122
    }
123
124
    /**
125
     * Updates openapi.yaml documentation file
126
     *
127
     * @return self
128
     */
129
    private function updateDocumentation(): self
130
    {
131
        $this->fileWriter->forceOverwrite()->write(
132
            EnvAccess::get('PROJECT_DIR') . '/openapi/openapi.yaml',
133
            $this->oaReader->read()->toYaml()
134
        );
135
136
        return $this;
137
    }
138
139
    /**
140
     * @return string[][][]
141
     * @throws \LogicException
142
     */
143
    private function resolveTemplateSchema(): array
144
    {
145
        if ($this->resourceReflection->implementsInterface(CollectionResourceInterface::class)) {
146
            return TemplateSchemas::COLLECTION_RESOURCE_SCHEMA;
147
        }
148
149
        if ($this->resourceReflection->implementsInterface(ResourceInterface::class)) {
150
            return TemplateSchemas::ITEM_RESOURCE_SCHEMA;
151
        }
152
153
        throw new \LogicException('Unknown resource type.');
154
    }
155
}
156