|
13 | 13 | DATA_MANAGEMENT_OPERATION_DRY_RUN, |
14 | 14 | DATA_MANAGEMENT_OPERATION_MIGRATION, |
15 | 15 | DATA_MANAGEMENT_OPERATION_RESTORE, |
| 16 | + DataManagementCosmosEditorError, |
16 | 17 | DataManagementSettingsValidationError, |
17 | 18 | generate_data_management_encryption_key, |
| 19 | + get_data_management_cosmos_editor_containers, |
| 20 | + get_data_management_cosmos_editor_document, |
18 | 21 | get_data_management_backup_summary, |
19 | 22 | get_data_management_job_detail, |
20 | 23 | get_data_management_jobs, |
21 | 24 | get_data_management_migration_catalog, |
22 | 25 | get_data_management_settings, |
| 26 | + log_data_management_cosmos_editor_activity, |
23 | 27 | queue_data_management_job, |
| 28 | + query_data_management_cosmos_editor_documents, |
24 | 29 | sanitize_data_management_job_for_admin, |
25 | 30 | sanitize_data_management_settings_for_admin, |
| 31 | + save_data_management_cosmos_editor_document, |
26 | 32 | summarize_data_management_migration_plan, |
27 | 33 | submit_data_management_job, |
28 | 34 | test_backup_storage_connection, |
@@ -58,6 +64,32 @@ def _log_data_management_admin_action(action, description, additional_context=No |
58 | 64 | ) |
59 | 65 |
|
60 | 66 |
|
| 67 | +def _get_cosmos_editor_payload(): |
| 68 | + payload = request.get_json(silent=True) or {} |
| 69 | + return payload if isinstance(payload, dict) else {} |
| 70 | + |
| 71 | + |
| 72 | +def _cosmos_editor_error_status(exc, default_status=400): |
| 73 | + status_code = getattr(exc, "status_code", None) |
| 74 | + if status_code == 404: |
| 75 | + return 404 |
| 76 | + if status_code == 412: |
| 77 | + return 409 |
| 78 | + return default_status |
| 79 | + |
| 80 | + |
| 81 | +def _log_cosmos_editor_failure(action, message, details=None): |
| 82 | + admin_user_id, admin_email = _get_admin_context() |
| 83 | + log_data_management_cosmos_editor_activity( |
| 84 | + admin_user_id, |
| 85 | + admin_email, |
| 86 | + action, |
| 87 | + "failed", |
| 88 | + message, |
| 89 | + details=details or {}, |
| 90 | + ) |
| 91 | + |
| 92 | + |
61 | 93 | def register_route_backend_data_management(bp): |
62 | 94 | @bp.route("/api/admin/data-management/settings", methods=["GET"]) |
63 | 95 | @swagger_route(security=get_auth_security()) |
@@ -201,6 +233,160 @@ def test_admin_data_management_target_enhanced_citation_storage(): |
201 | 233 | return jsonify({"success": False, "error": "Target Enhanced Citation Storage connection test failed."}), 400 |
202 | 234 | return jsonify(result), 200 |
203 | 235 |
|
| 236 | + @bp.route("/api/admin/data-management/cosmos-editor/containers", methods=["GET"]) |
| 237 | + @swagger_route(security=get_auth_security()) |
| 238 | + @login_required |
| 239 | + @admin_required |
| 240 | + def list_admin_data_management_cosmos_editor_containers(): |
| 241 | + return jsonify({ |
| 242 | + "success": True, |
| 243 | + "containers": get_data_management_cosmos_editor_containers(), |
| 244 | + }), 200 |
| 245 | + |
| 246 | + @bp.route("/api/admin/data-management/cosmos-editor/danger-acknowledgement", methods=["POST"]) |
| 247 | + @swagger_route(security=get_auth_security()) |
| 248 | + @login_required |
| 249 | + @admin_required |
| 250 | + def acknowledge_admin_data_management_cosmos_editor_danger(): |
| 251 | + admin_user_id, admin_email = _get_admin_context() |
| 252 | + log_data_management_cosmos_editor_activity( |
| 253 | + admin_user_id, |
| 254 | + admin_email, |
| 255 | + "cosmos_editor_danger_acknowledged", |
| 256 | + "success", |
| 257 | + "Acknowledged the Cosmos DB editor danger prompt.", |
| 258 | + {"prompt": "interface_entry"}, |
| 259 | + ) |
| 260 | + return jsonify({"success": True}), 200 |
| 261 | + |
| 262 | + @bp.route("/api/admin/data-management/cosmos-editor/query", methods=["POST"]) |
| 263 | + @swagger_route(security=get_auth_security()) |
| 264 | + @login_required |
| 265 | + @admin_required |
| 266 | + def query_admin_data_management_cosmos_editor_documents(): |
| 267 | + payload = _get_cosmos_editor_payload() |
| 268 | + admin_user_id, admin_email = _get_admin_context() |
| 269 | + try: |
| 270 | + result = query_data_management_cosmos_editor_documents( |
| 271 | + payload.get("container"), |
| 272 | + query_text=payload.get("query"), |
| 273 | + page_size=payload.get("page_size"), |
| 274 | + continuation_token=payload.get("continuation_token"), |
| 275 | + admin_user_id=admin_user_id, |
| 276 | + admin_email=admin_email, |
| 277 | + ) |
| 278 | + except DataManagementCosmosEditorError as exc: |
| 279 | + _log_cosmos_editor_failure( |
| 280 | + "cosmos_editor_query_rejected", |
| 281 | + "Rejected a Cosmos DB editor query.", |
| 282 | + {"container": payload.get("container"), "error": str(exc)}, |
| 283 | + ) |
| 284 | + return jsonify({"success": False, "error": "Cosmos DB editor query was rejected."}), 400 |
| 285 | + except Exception as exc: |
| 286 | + log_event( |
| 287 | + "[DataManagement] Cosmos editor query failed.", |
| 288 | + {"container": payload.get("container"), "error": str(exc)}, |
| 289 | + level=logging.WARNING, |
| 290 | + ) |
| 291 | + _log_cosmos_editor_failure( |
| 292 | + "cosmos_editor_query_failed", |
| 293 | + "Cosmos DB editor query failed.", |
| 294 | + {"container": payload.get("container"), "status_code": getattr(exc, "status_code", None), "error": str(exc)}, |
| 295 | + ) |
| 296 | + return jsonify({"success": False, "error": "Cosmos DB editor query failed."}), _cosmos_editor_error_status(exc) |
| 297 | + return jsonify({"success": True, **result}), 200 |
| 298 | + |
| 299 | + @bp.route("/api/admin/data-management/cosmos-editor/document", methods=["POST"]) |
| 300 | + @swagger_route(security=get_auth_security()) |
| 301 | + @login_required |
| 302 | + @admin_required |
| 303 | + def get_admin_data_management_cosmos_editor_document(): |
| 304 | + payload = _get_cosmos_editor_payload() |
| 305 | + admin_user_id, admin_email = _get_admin_context() |
| 306 | + try: |
| 307 | + result = get_data_management_cosmos_editor_document( |
| 308 | + payload.get("container"), |
| 309 | + payload.get("id"), |
| 310 | + payload.get("partition_key"), |
| 311 | + admin_user_id=admin_user_id, |
| 312 | + admin_email=admin_email, |
| 313 | + ) |
| 314 | + except DataManagementCosmosEditorError as exc: |
| 315 | + log_event( |
| 316 | + "[DataManagement] Cosmos editor document open rejected.", |
| 317 | + {"container": payload.get("container"), "document_id": payload.get("id"), "error": str(exc)}, |
| 318 | + level=logging.WARNING, |
| 319 | + ) |
| 320 | + _log_cosmos_editor_failure( |
| 321 | + "cosmos_editor_document_rejected", |
| 322 | + "Rejected a Cosmos DB editor document open request.", |
| 323 | + {"container": payload.get("container"), "document_id": payload.get("id"), "error": str(exc)}, |
| 324 | + ) |
| 325 | + return jsonify({"success": False, "error": "Cosmos DB document request was invalid."}), 400 |
| 326 | + except Exception as exc: |
| 327 | + status_code = _cosmos_editor_error_status(exc, default_status=400) |
| 328 | + log_event( |
| 329 | + "[DataManagement] Cosmos editor document open failed.", |
| 330 | + {"container": payload.get("container"), "document_id": payload.get("id"), "error": str(exc)}, |
| 331 | + level=logging.WARNING, |
| 332 | + ) |
| 333 | + _log_cosmos_editor_failure( |
| 334 | + "cosmos_editor_document_failed", |
| 335 | + "Cosmos DB editor document open failed.", |
| 336 | + {"container": payload.get("container"), "document_id": payload.get("id"), "status_code": getattr(exc, "status_code", None), "error": str(exc)}, |
| 337 | + ) |
| 338 | + error_message = "Cosmos DB document was not found." if status_code == 404 else "Cosmos DB document could not be opened." |
| 339 | + return jsonify({"success": False, "error": error_message}), status_code |
| 340 | + return jsonify({"success": True, **result}), 200 |
| 341 | + |
| 342 | + @bp.route("/api/admin/data-management/cosmos-editor/document", methods=["PUT"]) |
| 343 | + @swagger_route(security=get_auth_security()) |
| 344 | + @login_required |
| 345 | + @admin_required |
| 346 | + def save_admin_data_management_cosmos_editor_document(): |
| 347 | + payload = _get_cosmos_editor_payload() |
| 348 | + admin_user_id, admin_email = _get_admin_context() |
| 349 | + try: |
| 350 | + result = save_data_management_cosmos_editor_document( |
| 351 | + payload.get("container"), |
| 352 | + payload.get("id"), |
| 353 | + payload.get("partition_key"), |
| 354 | + payload.get("etag"), |
| 355 | + payload.get("document"), |
| 356 | + confirmation_accepted=payload.get("confirmation_accepted") is True, |
| 357 | + confirmation_phrase=payload.get("confirmation_phrase"), |
| 358 | + admin_user_id=admin_user_id, |
| 359 | + admin_email=admin_email, |
| 360 | + ) |
| 361 | + except DataManagementCosmosEditorError as exc: |
| 362 | + _log_cosmos_editor_failure( |
| 363 | + "cosmos_editor_save_rejected", |
| 364 | + "Rejected a Cosmos DB editor save request.", |
| 365 | + {"container": payload.get("container"), "document_id": payload.get("id"), "error": str(exc)}, |
| 366 | + ) |
| 367 | + return jsonify({"success": False, "error": "Cosmos DB document save request was rejected."}), 400 |
| 368 | + except Exception as exc: |
| 369 | + status_code = _cosmos_editor_error_status(exc, default_status=400) |
| 370 | + log_event( |
| 371 | + "[DataManagement] Cosmos editor save failed.", |
| 372 | + {"container": payload.get("container"), "document_id": payload.get("id"), "error": str(exc)}, |
| 373 | + level=logging.WARNING, |
| 374 | + exceptionTraceback=True, |
| 375 | + ) |
| 376 | + _log_cosmos_editor_failure( |
| 377 | + "cosmos_editor_save_failed", |
| 378 | + "Cosmos DB editor save failed.", |
| 379 | + {"container": payload.get("container"), "document_id": payload.get("id"), "status_code": getattr(exc, "status_code", None), "error": str(exc)}, |
| 380 | + ) |
| 381 | + if status_code == 409: |
| 382 | + error_message = "Cosmos DB document changed after it was opened. Refresh before saving again." |
| 383 | + elif status_code == 404: |
| 384 | + error_message = "Cosmos DB document was not found." |
| 385 | + else: |
| 386 | + error_message = "Cosmos DB document could not be saved." |
| 387 | + return jsonify({"success": False, "error": error_message}), status_code |
| 388 | + return jsonify({"success": True, **result}), 200 |
| 389 | + |
204 | 390 | @bp.route("/api/admin/data-management/jobs", methods=["GET"]) |
205 | 391 | @swagger_route(security=get_auth_security()) |
206 | 392 | @login_required |
|
0 commit comments