Files
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

74 lines
2.7 KiB
Python

from django.apps import AppConfig
class NotebooksConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "products.notebooks.backend"
label = "notebooks"
def ready(self) -> None:
from insights.api.file_system.deletion import (
register_file_system_type,
register_post_delete_hook,
register_post_restore_hook,
)
from insights.helpers.impersonation import is_impersonated
from insights.models.activity_logging.activity_log import Change
from insights.models.user import User
# Lives in its own module: the API module's imports reach the kernel runtime and the
# tasks sandbox (modal SDK), which must stay off the django.setup() path.
from products.notebooks.backend.activity_logging import log_notebook_activity
register_file_system_type(
"notebook",
"notebooks",
"Notebook",
lookup_field="short_id",
undo_message="Send PATCH /v1/projects/@current/notebooks/{id} with deleted=false.",
)
def _post_delete(context, notebook):
organization = context.organization
if not organization:
return
team = context.team
team_id = getattr(team, "id", None) if team is not None else None
if not isinstance(team_id, int):
return
user = context.user
if not isinstance(user, User):
return
log_notebook_activity(
activity="deleted",
notebook=notebook,
organization_id=organization.id,
team_id=team_id,
user=user,
was_impersonated=is_impersonated(context.request),
)
def _post_restore(context, notebook):
organization = context.organization
if not organization:
return
team = context.team
team_id = getattr(team, "id", None) if team is not None else None
if not isinstance(team_id, int):
return
user = context.user
if not isinstance(user, User):
return
log_notebook_activity(
activity="restored",
notebook=notebook,
organization_id=organization.id,
team_id=team_id,
user=user,
was_impersonated=is_impersonated(context.request),
changes=[Change(type="Notebook", action="changed", field="deleted", before=True, after=False)],
)
register_post_delete_hook("notebook", _post_delete)
register_post_restore_hook("notebook", _post_restore)