Refactor error handling and logging in API responses

This commit is contained in:
2026-06-03 17:16:14 +02:00
parent 30d3ab4a87
commit d0daa7e937
27 changed files with 808 additions and 220 deletions

View File

@ -9,9 +9,22 @@ switch ($method) {
case 'GET':
if (!empty($_GET['exportBundle'])) {
require_role(['admin', 'supervisor'], $tokenRec);
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
$bundle = qdb_export_translations_bundle($pdo);
qdb_discard($tmpDb, $lockFp);
try {
$opened = qdb_open_read_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
$bundle = qdb_export_translations_bundle($pdo);
qdb_discard($tmpDb, $lockFp);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations exportBundle: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Export'), 500);
}
$filename = 'translations_bundle_' . date('Y-m-d_His') . '.json';
header('Content-Type: application/json; charset=UTF-8');
@ -21,14 +34,29 @@ case 'GET':
}
if (isset($_GET['languages'])) {
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
$rows = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
json_success(["languages" => $rows]);
try {
$opened = qdb_open_read_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
$rows = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
json_success(['languages' => $rows]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations languages: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Load languages'), 500);
}
}
if (!empty($_GET['all'])) {
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
try {
$opened = qdb_open_read_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
$languages = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC);
if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) {
@ -65,41 +93,69 @@ case 'GET':
}
}
qdb_discard($tmpDb, $lockFp);
json_success([
'languages' => $languages,
'appStrings' => $appEntries,
'questionnaires' => $questionnaires,
'entries' => $allEntries,
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
]);
qdb_discard($tmpDb, $lockFp);
json_success([
'languages' => $languages,
'appStrings' => $appEntries,
'questionnaires' => $questionnaires,
'entries' => $allEntries,
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations all: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Load translations'), 500);
}
}
$qnID = $_GET['questionnaireID'] ?? '';
if ($qnID) {
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
$qnID = trim((string)($_GET['questionnaireID'] ?? ''));
if ($qnID !== '') {
try {
$opened = qdb_open_read_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
$languages = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC);
if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) {
array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German']);
}
$languages = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC);
if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) {
array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German']);
}
$qnRow = $pdo->prepare("SELECT name FROM questionnaire WHERE questionnaireID = :id");
$qnRow->execute([':id' => $qnID]);
$qnName = $qnRow->fetchColumn() ?: '';
$qnRow = $pdo->prepare('SELECT name FROM questionnaire WHERE questionnaireID = :id');
$qnRow->execute([':id' => $qnID]);
$qnName = $qnRow->fetchColumn();
if ($qnName === false) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Questionnaire not found', 404);
}
$lists = qdb_translation_entry_lists($pdo, $qnID);
$lists = qdb_translation_entry_lists($pdo, $qnID);
$entries = array_merge($lists['stringEntries'], $lists['contentEntries']);
$translations = qdb_load_translations_for_entries($pdo, $entries);
qdb_attach_translation_texts($entries, $translations);
qdb_discard($tmpDb, $lockFp);
json_success([
'languages' => $languages,
'entries' => $entries,
'questionnaire' => ['questionnaireID' => $qnID, 'name' => $qnName],
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
]);
qdb_discard($tmpDb, $lockFp);
json_success([
'languages' => $languages,
'entries' => $entries,
'questionnaire' => ['questionnaireID' => $qnID, 'name' => (string)$qnName],
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations questionnaire: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Load translations'), 500);
}
}
$type = $_GET['type'] ?? '';
@ -108,8 +164,10 @@ case 'GET':
json_error('BAD_REQUEST', 'type (question|answer_option|string) and id required', 400);
}
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
if ($type === 'question') {
try {
$opened = qdb_open_read_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
if ($type === 'question') {
$stmt = $pdo->prepare("SELECT languageCode, text FROM question_translation WHERE questionID = :id");
$stmt->execute([':id' => $id]);
} elseif ($type === 'answer_option') {
@ -123,9 +181,27 @@ case 'GET':
$stmt = $pdo->query("SELECT stringKey, languageCode, text FROM string_translation ORDER BY stringKey, languageCode");
}
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
json_success(["translations" => $rows]);
if ($type === 'question' || $type === 'answer_option') {
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Translation entity not found', 404);
}
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
json_success(['translations' => $rows]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations GET: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Load translations'), 500);
}
break;
case 'POST':
@ -144,7 +220,12 @@ case 'POST':
} catch (Throwable $e) {
qdb_discard($tmpDb, $lockFp);
error_log('translations importBundle: ' . $e->getMessage());
json_error('SERVER_ERROR', $e->getMessage(), 500);
require_once __DIR__ . '/../lib/errors.php';
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
error_log('translations importBundle: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Import'), 500);
}
break;
}
@ -188,15 +269,26 @@ case 'PUT':
json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400);
}
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
try {
$opened = qdb_open_write_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Translation entity not found', 404);
}
qdb_put_translation($pdo, $type, $id, $lang, $text);
qdb_save($tmpDb, $lockFp);
json_success([]);
} catch (Throwable $e) {
qdb_discard($tmpDb, $lockFp);
error_log($e->getMessage());
json_error('SERVER_ERROR', 'Server error', 500);
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations PUT: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Save translation'), 500);
}
break;
@ -235,24 +327,37 @@ case 'DELETE':
json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400);
}
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
try {
$opened = qdb_open_write_or_fail();
[$pdo, $tmpDb, $lockFp] = $opened;
if ($type === 'question' || $type === 'answer_option') {
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Translation entity not found', 404);
}
}
if ($type === 'question') {
$pdo->prepare("DELETE FROM question_translation WHERE questionID = :id AND languageCode = :lang")
$pdo->prepare('DELETE FROM question_translation WHERE questionID = :id AND languageCode = :lang')
->execute([':id' => $id, ':lang' => $lang]);
} elseif ($type === 'answer_option') {
$pdo->prepare("DELETE FROM answer_option_translation WHERE answerOptionID = :id AND languageCode = :lang")
$pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id AND languageCode = :lang')
->execute([':id' => $id, ':lang' => $lang]);
} else {
$pdo->prepare("DELETE FROM string_translation WHERE stringKey = :id AND languageCode = :lang")
$pdo->prepare('DELETE FROM string_translation WHERE stringKey = :id AND languageCode = :lang')
->execute([':id' => $id, ':lang' => $lang]);
}
qdb_save($tmpDb, $lockFp);
json_success([]);
} catch (Throwable $e) {
qdb_discard($tmpDb, $lockFp);
error_log($e->getMessage());
json_error('SERVER_ERROR', 'Server error', 500);
if (isset($tmpDb, $lockFp)) {
qdb_discard($tmpDb, $lockFp);
}
if (function_exists('qdb_api_log_note_exception')) {
qdb_api_log_note_exception($e);
}
require_once __DIR__ . '/../lib/errors.php';
error_log('translations DELETE: ' . $e->getMessage());
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Delete translation'), 500);
}
break;