mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
add resource api route
This commit is contained in:
@@ -6,11 +6,7 @@ from fastapi import FastAPI
|
||||
from loguru import logger
|
||||
|
||||
from basic_memory import db
|
||||
from .routers import knowledge
|
||||
from .routers import discovery
|
||||
from .routers import memory
|
||||
from .routers.activity_router import router as activity_router
|
||||
from .routers.search_router import router as search_router
|
||||
from .routers import knowledge, discovery, search, activity, memory, resource
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -33,7 +29,7 @@ app = FastAPI(
|
||||
# Include routers
|
||||
app.include_router(knowledge.router)
|
||||
app.include_router(discovery.router)
|
||||
app.include_router(activity_router)
|
||||
app.include_router(search_router)
|
||||
|
||||
app.include_router(memory.router)
|
||||
app.include_router(activity.router)
|
||||
app.include_router(search.router)
|
||||
app.include_router(memory.router)
|
||||
app.include_router(resource.router)
|
||||
@@ -3,5 +3,8 @@
|
||||
from . import knowledge_router as knowledge
|
||||
from . import discovery_router as discovery
|
||||
from . import memory_router as memory
|
||||
from . import resource_router as resource
|
||||
from . import activity_router as activity
|
||||
from . import search_router as search
|
||||
|
||||
__all__ = ["knowledge", "discovery", "memory"]
|
||||
__all__ = ["knowledge", "discovery", "memory", "resource", "activity", "search"]
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Routes for getting entity content."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from loguru import logger
|
||||
|
||||
from basic_memory.deps import EntityRepositoryDep, ProjectConfigDep
|
||||
|
||||
router = APIRouter(prefix="/resource", tags=["resources"])
|
||||
|
||||
|
||||
@router.get("/{permalink:path}")
|
||||
async def get_resource_content(
|
||||
config: ProjectConfigDep,
|
||||
entity_repository: EntityRepositoryDep,
|
||||
permalink: str,
|
||||
) -> FileResponse:
|
||||
"""Get resource content by permalink."""
|
||||
logger.debug(f"Getting content for permalink: {permalink}")
|
||||
|
||||
# Find entity by permalink
|
||||
entity = await entity_repository.get_by_permalink(permalink)
|
||||
if not entity:
|
||||
raise HTTPException(status_code=404, detail=f"Entity not found: {permalink}")
|
||||
|
||||
file_path = Path(f"{config.home}/{entity.file_path}")
|
||||
if not file_path.exists():
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"File not found: {file_path}",
|
||||
)
|
||||
return FileResponse(path=file_path)
|
||||
@@ -156,7 +156,7 @@ class SearchService:
|
||||
# We can construct these because observations are always
|
||||
# defined in and owned by a single entity
|
||||
observation_permalink = (
|
||||
f"{entity.permalink}/observations/{obs.category}/{generate_permalink(obs.content)}"
|
||||
generate_permalink(f"{entity.permalink}/observations/{obs.category}/{obs.content}")
|
||||
)
|
||||
|
||||
# Index with parent entity's file path since that's where it's defined
|
||||
@@ -186,7 +186,7 @@ class SearchService:
|
||||
# source/relation_type/target
|
||||
# e.g., "specs/search/implements/features/search-ui"
|
||||
relation_permalink = (
|
||||
f"{rel.from_entity.permalink}/{rel.relation_type}/{rel.to_entity.permalink}"
|
||||
generate_permalink(f"{rel.from_entity.permalink}/{rel.relation_type}/{rel.to_entity.permalink}")
|
||||
)
|
||||
|
||||
# Create descriptive title showing the relationship
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Tests for resource router endpoints."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_resource_content(client, test_config, entity_repository):
|
||||
"""Test getting content by permalink."""
|
||||
# Create a test file
|
||||
content = "# Test Content\n\nThis is a test file."
|
||||
test_file = Path(test_config.home) / "test" / "test.md"
|
||||
test_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
test_file.write_text(content)
|
||||
|
||||
# Create entity referencing the file
|
||||
entity = await entity_repository.create(
|
||||
{
|
||||
"title": "Test Entity",
|
||||
"entity_type": "test",
|
||||
"permalink": "test/test",
|
||||
"file_path": "test/test.md", # Relative to config.home
|
||||
"content_type": "text/markdown",
|
||||
}
|
||||
)
|
||||
|
||||
# Test getting the content
|
||||
response = await client.get(f"/resource/{entity.permalink}")
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "text/markdown; charset=utf-8"
|
||||
assert response.text == content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_resource_missing_entity(client):
|
||||
"""Test 404 when entity doesn't exist."""
|
||||
response = await client.get("/resource/does/not/exist")
|
||||
assert response.status_code == 404
|
||||
assert "Entity not found" in response.json()["detail"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_resource_missing_file(client, test_config, entity_repository):
|
||||
"""Test 404 when file doesn't exist."""
|
||||
# Create entity referencing non-existent file
|
||||
entity = await entity_repository.create(
|
||||
{
|
||||
"title": "Missing File",
|
||||
"entity_type": "test",
|
||||
"permalink": "test/missing",
|
||||
"file_path": "test/missing.md",
|
||||
"content_type": "text/markdown",
|
||||
}
|
||||
)
|
||||
|
||||
response = await client.get(f"/resource/{entity.permalink}")
|
||||
assert response.status_code == 404
|
||||
assert "File not found" in response.json()["detail"]
|
||||
Reference in New Issue
Block a user