* @brief Validates and forwards a project.batch payload, summarizing any per-op failures. */
| 1433 | * @brief Validates and forwards a project.batch payload, summarizing any per-op failures. |
| 1434 | */ |
| 1435 | static QJsonObject executeBulkApply(const QJsonObject& args) |
| 1436 | { |
| 1437 | const auto ops = args.value(QStringLiteral("ops")).toArray(); |
| 1438 | if (ops.isEmpty()) { |
| 1439 | QJsonObject out; |
| 1440 | out[QStringLiteral("ok")] = false; |
| 1441 | out[QStringLiteral("error")] = QStringLiteral("missing_ops"); |
| 1442 | return out; |
| 1443 | } |
| 1444 | |
| 1445 | if (ops.size() > 1024) { |
| 1446 | QJsonObject out; |
| 1447 | out[QStringLiteral("ok")] = false; |
| 1448 | out[QStringLiteral("error")] = QStringLiteral("too_many_ops"); |
| 1449 | out[QStringLiteral("hint")] = QStringLiteral("Split project.batch calls at 1024 ops."); |
| 1450 | return out; |
| 1451 | } |
| 1452 | |
| 1453 | for (const auto& value : ops) { |
| 1454 | const auto op = value.toObject(); |
| 1455 | const auto opCommand = op.value(QStringLiteral("command")).toString(); |
| 1456 | if (opCommand == QStringLiteral("project.batch")) { |
| 1457 | QJsonObject out; |
| 1458 | out[QStringLiteral("ok")] = false; |
| 1459 | out[QStringLiteral("error")] = QStringLiteral("nested_batch_rejected"); |
| 1460 | return out; |
| 1461 | } |
| 1462 | |
| 1463 | if (!innerOpAllowed(opCommand)) |
| 1464 | return makeInnerOpRejection(opCommand); |
| 1465 | } |
| 1466 | |
| 1467 | QJsonObject batchArgs; |
| 1468 | batchArgs[QStringLiteral("ops")] = ops; |
| 1469 | if (args.contains(QStringLiteral("stopOnError"))) |
| 1470 | batchArgs[QStringLiteral("stopOnError")] = args.value(QStringLiteral("stopOnError")).toBool(); |
| 1471 | |
| 1472 | auto raw = runCommand(QStringLiteral("project.batch"), batchArgs); |
| 1473 | if (!raw.value(QStringLiteral("ok")).toBool()) |
| 1474 | return attachRepairHint(raw, QStringLiteral("project.batch")); |
| 1475 | |
| 1476 | const auto batchResult = raw.value(QStringLiteral("result")).toObject(); |
| 1477 | const auto rows = batchResult.value(QStringLiteral("results")).toArray(); |
| 1478 | |
| 1479 | QJsonArray slimResults; |
| 1480 | QJsonArray failures; |
| 1481 | for (int i = 0; i < rows.size(); ++i) { |
| 1482 | const auto row = rows.at(i).toObject(); |
| 1483 | const bool success = |
| 1484 | row.value(QStringLiteral("success")).toBool(row.value(QStringLiteral("ok")).toBool(true)); |
| 1485 | |
| 1486 | QJsonObject slim; |
| 1487 | slim[QStringLiteral("index")] = i; |
| 1488 | slim[QStringLiteral("command")] = row.value(QStringLiteral("command")); |
| 1489 | slim[QStringLiteral("success")] = success; |
| 1490 | if (success) { |
| 1491 | const auto opResult = row.value(QStringLiteral("result")).toObject(); |
| 1492 | const auto identity = compactBatchRowResult(opResult); |
no test coverage detected