Skip to content
21 changes: 20 additions & 1 deletion backend/backend/application/context/no_code_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,29 @@
from typing import Any

from backend.application.context.application import ApplicationContext
from backend.core.models.config_models import ConfigModels
from backend.errors import InvalidModelConfigError


class NoCodeModel(ApplicationContext):
def __init__(self, project_id: str, environment_id: str = "") -> None:
super().__init__(project_id, environment_id)

def _reset_model_run_status(self, model_name: str) -> None:
"""Reset run status when the model spec changes so the explorer
doesn't keep showing a stale failure indicator."""
try:
model_instance = ConfigModels.objects.get(
project_instance__project_uuid=self.session.project_id,
model_name=model_name,
)
if model_instance.run_status == ConfigModels.RunStatus.FAILED:
model_instance.run_status = ConfigModels.RunStatus.NOT_STARTED
model_instance.failure_reason = None
model_instance.save(update_fields=["run_status", "failure_reason"])
except ConfigModels.DoesNotExist:
pass
Comment thread
abhizipstack marked this conversation as resolved.

def _validate_and_update_model(
self,
model_data: dict[str, Any],
Expand Down Expand Up @@ -49,7 +65,10 @@ def _validate_and_update_model(
config_type=config_type
)
# Converting the current model to python.
return self.update_model(model_name=model_name, model_data=model_data)
result = self.update_model(model_name=model_name, model_data=model_data)
# Reset stale run status so the explorer doesn't show the previous error
self._reset_model_run_status(model_name)
return result
Comment thread
greptile-apps[bot] marked this conversation as resolved.

def set_model_config_and_reference(self, no_code_data: dict[str, Any], model_name: str):
"""
Expand Down
11 changes: 8 additions & 3 deletions backend/backend/core/routers/execute/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from rest_framework.request import Request
from rest_framework.response import Response
from visitran.singleton import Singleton
from visitran.errors.base_exceptions import VisitranBaseExceptions
import logging

from backend.application.context.application import ApplicationContext
from backend.errors.visitran_backend_base_exceptions import VisitranBackendBaseException
from backend.core.utils import handle_http_request, sanitize_data
from backend.utils.cache_service.decorators.cache_decorator import clear_cache
from backend.utils.constants import HTTPMethods
Expand Down Expand Up @@ -63,10 +65,13 @@ def execute_run_command(request: Request, project_id: str) -> Response:
logger.info(f"[execute_run_command] Completed successfully for file_name={file_name}")
_data = {"status": "success"}
return Response(data=_data)
except Exception:
except (VisitranBaseExceptions, VisitranBackendBaseException) as err:
logger.exception(f"[execute_run_command] DAG execution failed for file_name={file_name}")
_data = {"status": "failed", "error_message": "Model execution failed. Check server logs for details."}
return Response(data=_data, status=status.HTTP_400_BAD_REQUEST)
return Response(data=err.error_response(), status=status.HTTP_400_BAD_REQUEST)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
except Exception:
logger.exception(f"[execute_run_command] Unexpected error during DAG execution for file_name={file_name}")
_data = {"status": "failed", "error_message": "An unexpected error occurred while executing the model. Please try again or contact support if the issue persists."}
return Response(data=_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
finally:
app.visitran_context.close_db_connection()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,14 @@ def severity(self) -> str:
return "Error"

def error_response(self) -> dict[str, Any]:
return {
response = {
"status": "failed",
"class": self.__class__.__name__,
"error_message": self.error_message,
"message_args": self._msg_args,
"severity": self.severity,
"is_markdown": self._is_markdown,
}
if "is_rollback" in self._msg_args:
response["is_rollback"] = self._msg_args["is_rollback"]
return response
5 changes: 4 additions & 1 deletion backend/visitran/errors/base_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ def __str__(self) -> str:
return self._error_msg

def error_response(self) -> dict[str, Any]:
return {
response = {
"status": "failed",
"error_message": self.error_message,
"message_args": self._msg_args,
"severity": self.severity,
"is_markdown": self._is_markdown,
}
if "is_rollback" in self._msg_args:
response["is_rollback"] = self._msg_args["is_rollback"]
return response
7 changes: 7 additions & 0 deletions frontend/src/service/notification-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ function NotificationProvider({ children }) {
return;
}

// Skip notification for JS runtime errors (not API failures)
// These have no useful info for the user — log them for debugging instead
if (error && !error.response && !message && !description) {
console.error("[Runtime Error]", error);
return;
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

const errorStatus = error?.response?.status;
const errorData = error?.response?.data;
if (errorData && typeof errorData.error_message === "string") {
Expand Down
Loading