add resource api route

This commit is contained in:
phernandez
2025-01-19 20:07:20 -06:00
parent 0b7b77f9e8
commit f61c5970df
5 changed files with 103 additions and 12 deletions
+5 -9
View File
@@ -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)
+4 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+58
View File
@@ -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"]