Passed
Push — main ( c49c6c...49fc69 )
by Gaetano
08:47
created

MigrationDefinitionExecutor::resolveReferencesRecursively()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 3
rs 10
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use JmesPath\Env as JmesPath;
6
use Kaliop\eZMigrationBundle\API\Exception\InvalidStepDefinitionException;
7
use Kaliop\eZMigrationBundle\API\Exception\MigrationBundleException;
8
use Kaliop\eZMigrationBundle\API\MatcherInterface;
9
use Kaliop\eZMigrationBundle\API\MigrationGeneratorInterface;
10
use Kaliop\eZMigrationBundle\API\ReferenceResolverBagInterface;
11
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
12
use Symfony\Component\Yaml\Yaml;
13
14
/**
15
 * @property ReferenceResolverBagInterface $referenceResolver
16
 */
17
class MigrationDefinitionExecutor extends AbstractExecutor
18
{
19
    use IgnorableStepExecutorTrait;
0 ignored issues
show
Bug introduced by
The trait Kaliop\eZMigrationBundle...orableStepExecutorTrait requires the property $dsl which is not provided by Kaliop\eZMigrationBundle...ationDefinitionExecutor.
Loading history...
20
    use ReferenceSetterTrait;
21
22
    protected $supportedStepTypes = array('migration_definition');
23
    protected $supportedActions = array('generate', 'save');
24
25
    /** @var \Kaliop\eZMigrationBundle\Core\MigrationService $migrationService */
26 149
    protected $migrationService;
27
28 149
    public function __construct($migrationService, ReferenceResolverBagInterface $referenceResolver)
29 149
    {
30 149
        $this->migrationService = $migrationService;
31
        $this->referenceResolver = $referenceResolver;
32
    }
33
34
    /**
35
     * @param MigrationStep $step
36
     * @return mixed
37 3
     * @throws \Exception
38
     */
39 3
    public function execute(MigrationStep $step)
40
    {
41 3
        parent::execute($step);
42
43
        if (!isset($step->dsl['mode'])) {
44
            throw new InvalidStepDefinitionException("Invalid step definition: missing 'mode'");
45 3
        }
46
47 3
        $action = $step->dsl['mode'];
48
49
        if (!in_array($action, $this->supportedActions)) {
50
            throw new InvalidStepDefinitionException("Invalid step definition: value '$action' is not allowed for 'mode'");
51 3
        }
52
53 3
        $this->skipStepIfNeeded($step);
54
55
        return $this->$action($step->dsl, $step->context);
56
    }
57
58
    /**
59
     * @todo allow to save to disk
60
     * @param array $dsl
61
     * @param array $context
62
     * @return array
63 3
     * @throws \Exception
64
     */
65 3
    protected function generate($dsl, $context)
66
    {
67
        if (!isset($dsl['migration_type'])) {
68 3
            throw new InvalidStepDefinitionException("Invalid step definition: miss 'migration_type'");
69 3
        }
70
        $migrationType = $this->resolveReference($dsl['migration_type']);
71
        if (!isset($dsl['migration_mode'])) {
72 3
            throw new InvalidStepDefinitionException("Invalid step definition: miss 'migration_mode'");
73 3
        }
74
        $migrationMode = $this->resolveReference($dsl['migration_mode']);
75
        if (!isset($dsl['match']) || !is_array($dsl['match'])) {
76 3
            throw new InvalidStepDefinitionException("Invalid step definition: miss 'match' to determine what to generate migration definition for");
77
        }
78 3
        $match = $dsl['match'];
79 3
80
        $executors = $this->getGeneratingExecutors();
81
        if (!in_array($migrationType, $executors)) {
82
            throw new InvalidStepDefinitionException("It is not possible to generate a migration of type '$migrationType': executor not found or not a generator");
83 3
        }
84
        /** @var MigrationGeneratorInterface $executor */
85 3
        $executor = $this->migrationService->getExecutor($migrationType);
86
87
        if (isset($dsl['lang']) && $dsl['lang'] != '') {
88
            $context['defaultLanguageCode'] = $this->resolveReference($dsl['lang']);
89
        }
90 3
91
        // in case the executor does different things based on extra information present in the step definition
92 3
        $context['step'] = $dsl;
93 3
94
        // q: should we use resolveReferenceRecursively for $match['value']?
95
        $matchCondition = array($this->resolveReference($match['type']) => $this->resolveReference($match['value']));
96
        if (isset($match['except']) && $match['except']) {
97 3
            $matchCondition = array(MatcherInterface::MATCH_NOT => $matchCondition);
98
        }
99 3
100
        $result = $executor->generateMigration($matchCondition, $migrationMode, $context);
101 3
102
        if (isset($dsl['file'])) {
103 3
104
            $fileName = $this->resolveReference($dsl['file']);
105
106 3
            $this->saveDefinition($result, $fileName);
107
        }
108 3
109
        $this->setReferences($result, $dsl);
110
111 1
        return $result;
112
    }
113 1
114
    public function save($dsl, $context)
115
    {
116 1
        if (!isset($dsl['migration_steps'])) {
117
            throw new InvalidStepDefinitionException("Invalid step definition: miss 'migration_steps'");
118
        }
119
        if (!isset($dsl['file'])) {
120 1
            throw new InvalidStepDefinitionException("Invalid step definition: miss 'file'");
121
        }
122
123 1
        if (is_string($dsl['migration_steps'])) {
124
            $definition = $this->resolveReference($dsl['migration_steps']);
125
        } else {
126 1
            $definition = $dsl['migration_steps'];
127
        }
128 1
129
        /// @todo allow resolving references within texts, not only as full value
130 1
        $definition = $this->resolveReferencesRecursively($definition);
131
132
        $fileName = $this->resolveReference($dsl['file']);
133
134
        $this->saveDefinition($definition, $fileName);
135 1
136
        /// @todo what to allow setting refs to ?
137
138 3
        /// @todo what to return ?
139
    }
140 3
141
    /// @todo move to a Loader service
142 3
    protected function saveDefinition($definition, $fileName)
143 3
    {
144 3
        $ext = pathinfo(basename($fileName), PATHINFO_EXTENSION);
145
146
        switch ($ext) {
147
            case 'yml':
148 3
            case 'yaml':
149 3
                /// @todo use Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE option if it is supported
150 3
                $code = Yaml::dump($definition, 5);
151
                break;
152
            case 'json':
153
                $code = json_encode($definition, JSON_PRETTY_PRINT);
154
                break;
155 3
            default:
156 3
                throw new InvalidStepDefinitionException("Can not save migration definition to a file of type '$ext'");
157
        }
158
159
        $dir = dirname($fileName);
160 3
        if (!is_dir($dir)) {
161
            mkdir($dir, 0777, true);
162
        }
163 3
164
        if (!file_put_contents($fileName, $code)) {
165 3
            throw new MigrationBundleException("Failed saving migration definition to file '$fileName'");
166
        }
167 3
    }
168 1
169
    protected function setReferences($result, $dsl)
170
    {
171 2
        if (!array_key_exists('references', $dsl) || !count($dsl['references'])) {
172
            return false;
173 2
        }
174
175
        foreach ($dsl['references'] as $key => $reference) {
176 2
            // BC
177 2
            if (is_array($reference) && isset($reference['json_path']) && !isset($reference['attribute'] )) {
178 2
                $reference['attribute'] = $reference['json_path'];
179
            }
180
            $reference = $this->parseReferenceDefinition($key, $reference);
181
            switch ($reference['attribute']) {
182 2
                case 'definition':
183
                    $value = $result;
184
                    break;
185 2
                default:
186 2
                    $value = JmesPath::search($reference['attribute'], $result);
187
            }
188
189
            $overwrite = false;
190 2
            if (isset($reference['overwrite'])) {
191
                $overwrite = $reference['overwrite'];
192 2
            }
193
194
            $this->addReference($reference['identifier'], $value, $overwrite);
195
        }
196
197
        return true;
198 3
    }
199
200 3
    /**
201 3
     * @todo cache this for faster access
202 3
     * @return string[]
203 3
     */
204 3
    protected function getGeneratingExecutors()
205 3
    {
206
        $migrationService = $this->migrationService;
207
        $executors = $migrationService->listExecutors();
208 3
        foreach ($executors as $key => $name) {
209
            $executor = $migrationService->getExecutor($name);
210
            if (!$executor instanceof MigrationGeneratorInterface) {
211
                unset($executors[$key]);
212
            }
213
        }
214
        return $executors;
215 1
    }
216
}
217