Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
18.99% |
49 / 258 |
|
15.00% |
3 / 20 |
CRAP | |
0.00% |
0 / 1 |
InitDAO | |
18.99% |
49 / 258 |
|
15.00% |
3 / 20 |
1073.17 | |
0.00% |
0 / 1 |
createSampleLoginsReviewsLogs | |
0.00% |
0 / 52 |
|
0.00% |
0 / 1 |
2 | |||
createSampleExpiredSessions | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
2 | |||
createSampleWorkspaceAdmins | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
createSampleMonitorSessions | |
0.00% |
0 / 49 |
|
0.00% |
0 / 1 |
2 | |||
createAdmin | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
createWorkspace | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
addWorkspacesToAdmin | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
clearDB | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
cloneDB | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getDbStatus | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
42 | |||
getTableStatus | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
createSampleCommands | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
importScanImage | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
adminExists | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
createWorkspaceIfMissing | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
installPatches | |
80.65% |
25 / 31 |
|
0.00% |
0 / 1 |
9.59 | |||
setDBSchemaVersion | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
getDBContentDump | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
createSampleMetaData | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
checkSQLMode | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** @noinspection PhpUnhandledExceptionInspection */ |
4 | declare(strict_types=1); |
5 | // TODO unit tests |
6 | /* TODO refactor: this should not *be* a DAO, because it initializes several DAOs itself, it should |
7 | - inherit from the controller maybe instead of DAO |
8 | - can be merged with WorkspaceInitializer maybe (since files and db management is not separable anymore anyways) |
9 | */ |
10 | |
11 | class InitDAO extends SessionDAO { |
12 | const legacyTableNames = [ |
13 | 'admintokens', |
14 | 'persons', |
15 | 'logins', |
16 | 'bookletlogs', |
17 | 'bookletreviews', |
18 | 'booklets', |
19 | 'unitlogs', |
20 | 'unitreviews' |
21 | ]; |
22 | |
23 | public function createSampleLoginsReviewsLogs(): void { |
24 | $timestamp = TimeStamp::now(); |
25 | |
26 | $sessionDAO = new SessionDAO(); |
27 | $testDAO = new TestDAO(); |
28 | |
29 | $testLogin = new Login( |
30 | 'test', |
31 | Password::encrypt('user123', $this->passwordSalt), |
32 | 'run-hot-return', |
33 | 'sample_group', |
34 | 'sample_group', |
35 | ['xxx' => ['BOOKLET.SAMPLE-1']], |
36 | 1, |
37 | TimeStamp::fromXMLFormat('1/1/2030 12:00'), |
38 | 0, |
39 | 0, |
40 | (object) ['somStr' => 'someLabel'] |
41 | ); |
42 | $loginSession = $sessionDAO->createLoginSession($testLogin); |
43 | |
44 | $personSession = $sessionDAO->createOrUpdatePersonSession($loginSession, 'xxx'); |
45 | $test = $testDAO->createTest($personSession->getPerson()->getId(), 'BOOKLET.SAMPLE-1', 'Sample Booklet 1'); |
46 | $testDAO->setTestRunning($test->id); |
47 | $testDAO->addTestReview( |
48 | $test->id, |
49 | 1, |
50 | "content", |
51 | "luigi: sample booklet review", |
52 | 'Firefox/126.0' |
53 | ); |
54 | $testDAO->addUnitReview( |
55 | $test->id, |
56 | "UNIT.SAMPLE", |
57 | 1, |
58 | "content", |
59 | "mario: this is a sample unit review", |
60 | 'Firefox/126.0', |
61 | "UNIT.SAMPLE", |
62 | 1, |
63 | 'page-1', |
64 | ); |
65 | $testDAO->addUnitLog($test->id, 'UNIT.SAMPLE', "sample unit log", $timestamp); |
66 | $testDAO->addTestLog($test->id, "sample log entry", $timestamp); |
67 | $testDAO->updateDataParts( |
68 | $test->id, |
69 | 'UNIT.SAMPLE', |
70 | ["all" => "{\"name\":\"Sam Sample\",\"age\":34}"], |
71 | "example-data-format", |
72 | $timestamp |
73 | ); |
74 | $testDAO->updateUnitState($test->id, "UNIT.SAMPLE", ["PRESENTATIONCOMPLETE" => "yes"]); |
75 | $testDAO->updateTestState($test->id, ["CURRENT_UNIT_ID" => "UNIT.SAMPLE"]); |
76 | $test2 = $testDAO->createTest($personSession->getPerson()->getId(), 'BOOKLET.SAMPLE-2', 'Sample Booklet 2'); |
77 | $testDAO->lockTest($test2->id); |
78 | $testDAO->setTestRunning($test2->id); |
79 | } |
80 | |
81 | public function createSampleExpiredSessions(): void { |
82 | $login = new Login( |
83 | 'test-expired', |
84 | '', |
85 | 'run-hot-return', |
86 | 'expired_group', |
87 | 'expired_group', |
88 | ['xxx' => ['BOOKLET.SAMPLE-1']], |
89 | 1, |
90 | TimeStamp::fromXMLFormat('1/1/2000 12:00') |
91 | ); |
92 | $login = $this->createLoginSession($login); |
93 | $this->createOrUpdatePersonSession($login, 'xxx', true); |
94 | } |
95 | |
96 | public function createSampleWorkspaceAdmins(): void { |
97 | $superAdminDAO = new SuperAdminDAO(); |
98 | $adminDAO = new AdminDAO(); |
99 | $superAdminDAO->createUser("workspace_admin", "anotherPassword"); |
100 | $adminDAO->createAdminToken("workspace_admin", "anotherPassword", TimeStamp::fromXMLFormat('1/1/2000 12:00')); |
101 | $superAdminDAO->createUser("expired_user", "whatever", true); |
102 | $adminDAO->createAdminToken("expired_user", "whatever", TimeStamp::fromXMLFormat('1/1/2000 12:00')); |
103 | } |
104 | |
105 | public function createSampleMonitorSessions(): array { |
106 | $personsSessions = []; |
107 | |
108 | $login = new Login( |
109 | 'test-group-monitor', |
110 | 'user123', |
111 | 'monitor-group', |
112 | 'sample_group', |
113 | 'sample_group', |
114 | [], |
115 | 1, |
116 | TimeStamp::fromXMLFormat('1/1/2030 12:00') |
117 | ); |
118 | $loginSession = $this->createLoginSession($login); |
119 | $personsSessions['test-group-monitor'] = $this->createOrUpdatePersonSession($loginSession, ''); |
120 | |
121 | $login = new Login( |
122 | 'expired-group-monitor', |
123 | 'user123', |
124 | 'monitor-group', |
125 | 'expired_group', |
126 | 'expired_group', |
127 | ['' => ['']], |
128 | 1, |
129 | TimeStamp::fromXMLFormat('1/1/2000 12:00') |
130 | ); |
131 | $loginSession = $this->createLoginSession($login); |
132 | $personsSessions['expired-group-monitor'] = $this->createOrUpdatePersonSession($loginSession, '', true); |
133 | |
134 | $login = new Login( |
135 | 'test-study-monitor', |
136 | 'user123', |
137 | 'monitor-study', |
138 | 'study_group', |
139 | "A group for the study monitor", |
140 | [], |
141 | 1 |
142 | ); |
143 | $loginSession = $this->createLoginSession($login); |
144 | $personsSessions['test-study-monitor'] = $this->createOrUpdatePersonSession($loginSession, ''); |
145 | |
146 | $login = new Login( |
147 | 'expired-study-monitor', |
148 | 'user123', |
149 | 'monitor-study', |
150 | 'expired_group', |
151 | 'expired_group', |
152 | ['' => ['']], |
153 | 1, |
154 | TimeStamp::fromXMLFormat('1/1/2000 12:00') |
155 | ); |
156 | $loginSession = $this->createLoginSession($login); |
157 | $personsSessions['expired-study-monitor'] = $this->createOrUpdatePersonSession($loginSession, '', true); |
158 | |
159 | return $personsSessions; |
160 | } |
161 | |
162 | public function createAdmin(string $username, string $password): int { |
163 | $superAdminDAO = new SuperAdminDAO(); |
164 | $admin = $superAdminDAO->createUser($username, $password, true); |
165 | $adminDAO = new AdminDAO(); |
166 | $adminDAO->createAdminToken($username, $password); // TODO why? |
167 | return (int) $admin['id']; |
168 | } |
169 | |
170 | public function createWorkspace(string $workspaceName): int { |
171 | $superAdminDAO = new SuperAdminDAO(); |
172 | $workspace = $superAdminDAO->createWorkspace($workspaceName); |
173 | return (int) $workspace['id']; |
174 | } |
175 | |
176 | public function addWorkspacesToAdmin(int $adminId, array $workspaceIds): void { |
177 | $superAdminDAO = new SuperAdminDAO(); |
178 | $superAdminDAO->setWorkspaceRightsByUser( |
179 | $adminId, |
180 | array_map( |
181 | function (int $wsId) { |
182 | return (object) [ |
183 | "role" => "RW", |
184 | "id" => $wsId |
185 | ]; |
186 | }, |
187 | $workspaceIds |
188 | ) |
189 | ); |
190 | } |
191 | |
192 | public function clearDB(): array { |
193 | $droppedTables = []; |
194 | |
195 | $this->_('SET FOREIGN_KEY_CHECKS = 0'); |
196 | |
197 | foreach (array_merge($this::legacyTableNames, $this::tables) as $table) { |
198 | if ($this->getTableStatus($table) !== 'missing') { |
199 | $droppedTables[] = $table; |
200 | $this->_("drop table $table"); |
201 | } |
202 | } |
203 | |
204 | $this->_('SET FOREIGN_KEY_CHECKS = 1'); |
205 | |
206 | return $droppedTables; |
207 | } |
208 | |
209 | public function cloneDB(string $prodDBName): void { |
210 | $this->clearDB(); |
211 | |
212 | foreach ($this::tables as $table) { |
213 | $creationString = $this->_("show create table $prodDBName.$table")['Create Table']; |
214 | $this->_($creationString); |
215 | $this->_("truncate $table"); // to reset auto-increment |
216 | } |
217 | } |
218 | |
219 | // TODO unit-test |
220 | public function getDbStatus(): array { |
221 | $tableStatus = [ |
222 | 'used' => [], |
223 | 'missing' => [], |
224 | 'empty' => [] |
225 | ]; |
226 | |
227 | foreach ($this::tables as $table) { |
228 | $tableStatus[$this->getTableStatus($table)][] = $table; |
229 | } |
230 | |
231 | $used = count($tableStatus['used']); |
232 | $missing = count($tableStatus['missing']); |
233 | $tables = $missing |
234 | ? ($missing == count($this::tables) ? 'empty' : 'incomplete') |
235 | : 'complete'; |
236 | |
237 | return [ |
238 | 'message' => $tables |
239 | . ". \nMissing Tables: " |
240 | . ($missing ? implode(', ', $tableStatus['missing']) : 'none') |
241 | . ". \nUsed Tables: " |
242 | . ($used ? implode(', ', $tableStatus['used']) : 'none') |
243 | . '.', |
244 | 'used' => !!$used, |
245 | 'tables' => $tables |
246 | ]; |
247 | } |
248 | |
249 | protected function getTableStatus(string $table): string { |
250 | try { |
251 | $entries = $this->_("SELECT * FROM $table limit 10", [], true); |
252 | return count($entries) ? 'used' : 'empty'; |
253 | |
254 | } catch (Exception) { |
255 | return 'missing'; |
256 | } |
257 | } |
258 | |
259 | public function createSampleCommands(int $commanderId): void { |
260 | $adminDAO = new AdminDAO(); |
261 | $adminDAO->storeCommand($commanderId, 1, new Command(-1, 'COMMAND', 1597906980, 'p4')); |
262 | $adminDAO->storeCommand($commanderId, 1, new Command(-1, 'COMMAND', 1597906970, 'p3')); |
263 | $adminDAO->storeCommand($commanderId, 1, new Command(-1, 'COMMAND', 1597906960, 'p1', 'p2')); |
264 | } |
265 | |
266 | public function importScanImage(int $workspaceId, string $imagePath): void { |
267 | $adminDAO = new AdminDAO(); |
268 | $attachment = $adminDAO->getAttachmentById("$workspaceId:UNIT.SAMPLE:v2"); |
269 | AttachmentFiles::importFiles($workspaceId, [$imagePath], $attachment, 'image'); |
270 | } |
271 | |
272 | public function adminExists(): bool { |
273 | $admins = $this->_("select count(*) as count from users where is_superadmin = 1"); |
274 | return (int) $admins['count'] > 0; |
275 | } |
276 | |
277 | public function createWorkspaceIfMissing(Workspace $workspace): array { |
278 | $workspaceFromDb = $this->_( |
279 | "select workspaces.id, workspaces.name from workspaces where `id` = :ws_id", |
280 | [':ws_id' => $workspace->getId()] |
281 | ); |
282 | |
283 | if ($workspaceFromDb) { |
284 | return $workspaceFromDb; |
285 | } |
286 | |
287 | $name = "ws {$workspace->getId()} [restored " . TimeStamp::toSQLFormat(TimeStamp::now()) . "]"; |
288 | |
289 | $this->_( |
290 | 'insert into workspaces (name, id) values (:ws_name, :ws_id)', |
291 | [':ws_name' => $name, ':ws_id' => $workspace->getId()] |
292 | ); |
293 | |
294 | return [ |
295 | "name" => $name, |
296 | "restored" => true, |
297 | "id" => $workspace->getId() |
298 | ]; |
299 | } |
300 | |
301 | public function installPatches(string $patchesDir, bool $allowFailing): array { |
302 | $report = [ |
303 | 'patches' => [], |
304 | 'errors' => [] |
305 | ]; |
306 | |
307 | $patches = array_map( |
308 | function ($file) { |
309 | return basename($file, '.sql'); |
310 | }, |
311 | Folder::glob($patchesDir, '*.sql') |
312 | ); |
313 | usort($patches, [Version::class, 'compare']); |
314 | |
315 | $nextPatchAvailable = ($patches[0] == 'next'); |
316 | if ($nextPatchAvailable) { |
317 | $patches[] = array_shift($patches); |
318 | } |
319 | |
320 | $patchIsFutureVersion = true; |
321 | |
322 | foreach ($patches as $patch) { |
323 | $lastWasFutureVersion = $patchIsFutureVersion; |
324 | $patchIsFutureVersion = Version::compare($patch) > 0; |
325 | $shouldBeInstalled = Version::compare($patch, $this->getDBSchemaVersion()) <= 0; |
326 | $forcePatch = ($patch == 'next') && !$lastWasFutureVersion; |
327 | |
328 | if ( |
329 | (!$forcePatch) && |
330 | ($patchIsFutureVersion or $shouldBeInstalled) |
331 | ) { |
332 | continue; |
333 | } |
334 | |
335 | try { |
336 | $report['patches'][] = $patch; |
337 | $this->runFile("$patchesDir/$patch.sql"); |
338 | $this->setDBSchemaVersion($patch); |
339 | |
340 | } catch (PDOException $exception) { |
341 | $report['errors'][$patch] = $exception->getMessage(); |
342 | |
343 | if (!$allowFailing) { |
344 | return $report; |
345 | } |
346 | } |
347 | } |
348 | |
349 | return $report; |
350 | } |
351 | |
352 | public function setDBSchemaVersion(string $newVersion): void { |
353 | $currentDBSchemaVersion = $this->getDBSchemaVersion(); |
354 | |
355 | if ($currentDBSchemaVersion == '0.0.0-no-table') { |
356 | return; |
357 | } |
358 | |
359 | if ($currentDBSchemaVersion == '0.0.0-no-entry') { |
360 | $this->_( |
361 | "insert into meta (metaKey, value) values ('dbSchemaVersion', :new_version)", |
362 | [':new_version' => $newVersion] |
363 | ); |
364 | } else { |
365 | $this->_( |
366 | "update meta set value = :new_version where metaKey = 'dbSchemaVersion'", |
367 | [':new_version' => $newVersion] |
368 | ); |
369 | } |
370 | } |
371 | |
372 | public function getDBContentDump(bool $includeLegacyTables = false): array { |
373 | $tables = $includeLegacyTables ? array_merge($this::legacyTableNames, $this::tables) : $this::tables; |
374 | |
375 | $report = []; |
376 | |
377 | foreach ($tables as $table) { |
378 | try { |
379 | $entries = $this->_("SELECT * FROM $table", [], true); |
380 | $report[$table] = CSV::build($entries); |
381 | } catch (Exception) { |
382 | $report[$table] = 'not found'; |
383 | } |
384 | } |
385 | |
386 | return $report; |
387 | } |
388 | |
389 | public function createSampleMetaData(): void { |
390 | $this->setMeta('appConfig', 'aKey', 'newValue'); |
391 | } |
392 | |
393 | public function checkSQLMode(): bool { |
394 | $d = $this->_("select 'a' || 'b' as merged")['merged']; |
395 | return $d === 'ab'; |
396 | } |
397 | } |