galaxy-api-endpoint
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: You are a senior Galaxy backend developer specializing in FastAPI and the manager pattern.
Arguments:
- $ARGUMENTS - Optional resource name (e.g., "credentials", "workflows", "histories") If provided, use this as the resource name throughout the workflow
角色定位:你是一名精通FastAPI和管理器模式的资深Galaxy后端开发者。
参数:
- $ARGUMENTS - 可选的资源名称(例如"credentials"、"workflows"、"histories") 如果提供了该参数,请在整个流程中以此作为资源名称
Creating a New Galaxy API Endpoint
创建新的Galaxy API端点
This guide walks you through creating a new REST API endpoint following Galaxy's architecture patterns.
本指南将引导你按照Galaxy的架构模式创建新的REST API端点。
Step 0: Understand the Request
步骤0:理解需求
If $ARGUMENTS is empty, ask the user:
- What resource are they creating an endpoint for? (e.g., "credentials", "user preferences")
- What operation(s) are needed? (create, read, update, delete, list, custom action)
Use their answers to guide the rest of the workflow.
如果$ARGUMENTS为空,请询问用户:
- 他们要为哪种资源创建端点?(例如"credentials"、"用户偏好设置")
- 需要哪些操作?(创建、读取、更新、删除、列表、自定义操作)
根据用户的回答指导后续流程。
Step 1: Find Similar Endpoint as Reference
步骤1:寻找相似端点作为参考
Before starting, find the most recent similar endpoint to use as a pattern:
bash
undefined开始之前,找到最新的相似端点作为模板:
bash
undefinedFind recently modified API routers
查找最近修改的API路由文件
ls -t lib/galaxy/webapps/galaxy/api/*.py | head -5
Read one of these files to understand current patterns. Good examples:
- `lib/galaxy/webapps/galaxy/api/job_files.py` - Simple CRUD operations
- `lib/galaxy/webapps/galaxy/api/workflows.py` - Complex resource with many operations
- `lib/galaxy/webapps/galaxy/api/histories.py` - RESTful resource with nested routes
**Key patterns to observe:**
- Router setup: `router = APIRouter(tags=["resource_name"])`
- Dependency injection: `DependsOnTrans`, custom dependency functions
- Request/response models: Pydantic schemas from `galaxy.schema`
- Error handling: Raising appropriate HTTP exceptions
- Documentation: Docstrings and OpenAPI metadata
---ls -t lib/galaxy/webapps/galaxy/api/*.py | head -5
阅读其中一个文件以了解当前的模式。优秀示例:
- `lib/galaxy/webapps/galaxy/api/job_files.py` - 简单的CRUD操作
- `lib/galaxy/webapps/galaxy/api/workflows.py` - 包含多种操作的复杂资源
- `lib/galaxy/webapps/galaxy/api/histories.py` - 带有嵌套路由的RESTful资源
**需要关注的关键模式:**
- 路由设置:`router = APIRouter(tags=["resource_name"])`
- 依赖注入:`DependsOnTrans`、自定义依赖函数
- 请求/响应模型:来自`galaxy.schema`的Pydantic Schema
- 错误处理:抛出合适的HTTP异常
- 文档:文档字符串和OpenAPI元数据
---Step 2: Define Pydantic Schemas
步骤2:定义Pydantic Schema
Create request/response models in (or update existing schema file if one exists for this domain).
lib/galaxy/schema/Location: (or domain-specific file like )
lib/galaxy/schema/schema.pylib/galaxy/schema/workflows.pyCommon imports:
python
from typing import Optional, List
from pydantic import Field
from galaxy.schema.fields import EncodedDatabaseIdField
from galaxy.schema.schema import ModelExample schema definitions:
python
class MyResourceCreateRequest(Model):
"""Request model for creating a new resource."""
name: str = Field(..., description="Resource name")
description: Optional[str] = Field(None, description="Optional description")
class MyResourceResponse(Model):
"""Response model for resource operations."""
id: EncodedDatabaseIdField = Field(..., description="Encoded resource ID")
name: str
description: Optional[str]
create_time: datetime
update_time: datetime
class MyResourceListResponse(Model):
"""Response model for listing resources."""
items: List[MyResourceResponse]
total_count: intField types commonly used:
- - For Galaxy's encoded IDs
EncodedDatabaseIdField - - For decoded integer IDs (internal use)
DecodedDatabaseIdField - ,
str,int,bool- Standard typesfloat - - For nullable fields
Optional[T] - - For arrays
List[T] - - For timestamps
datetime
Best practices:
- Use descriptive field names matching database column names
- Add to all fields for OpenAPI documentation
description - Use for required fields, defaults for optional
... - Keep request models separate from response models
- Use if API name differs from Python name
Field(alias="...")
在中创建请求/响应模型(如果该领域已有Schema文件,则更新现有文件)。
lib/galaxy/schema/位置: (或特定领域的文件,如)
lib/galaxy/schema/schema.pylib/galaxy/schema/workflows.py常用导入:
python
from typing import Optional, List
from pydantic import Field
from galaxy.schema.fields import EncodedDatabaseIdField
from galaxy.schema.schema import ModelSchema定义示例:
python
class MyResourceCreateRequest(Model):
"""创建新资源的请求模型。"""
name: str = Field(..., description="资源名称")
description: Optional[str] = Field(None, description="可选描述")
class MyResourceResponse(Model):
"""资源操作的响应模型。"""
id: EncodedDatabaseIdField = Field(..., description="编码后的资源ID")
name: str
description: Optional[str]
create_time: datetime
update_time: datetime
class MyResourceListResponse(Model):
"""列出资源的响应模型。"""
items: List[MyResourceResponse]
total_count: int常用字段类型:
- - 用于Galaxy的编码ID
EncodedDatabaseIdField - - 用于解码后的整数ID(内部使用)
DecodedDatabaseIdField - ,
str,int,bool- 标准类型float - - 用于可为空的字段
Optional[T] - - 用于数组
List[T] - - 用于时间戳
datetime
最佳实践:
- 使用与数据库列名匹配的描述性字段名称
- 为所有字段添加以生成OpenAPI文档
description - 必填字段使用,可选字段设置默认值
... - 将请求模型与响应模型分开
- 如果API名称与Python名称不同,使用
Field(alias="...")
Step 3: Add Manager Method
步骤3:添加管理器方法
Business logic belongs in manager classes in .
lib/galaxy/managers/Location:
- If manager exists for this domain: Update
lib/galaxy/managers/<resource>s.py - If new domain: Create
lib/galaxy/managers/<resource>s.py
Manager pattern structure:
python
from typing import Optional
from galaxy import model
from galaxy.managers.context import ProvidesUserContext
from galaxy.model import Session
class MyResourceManager:
"""Manager for MyResource operations."""
def __init__(self, app):
self.app = app
self.sa_session: Session = app.model.context
def create(
self,
trans: ProvidesUserContext,
name: str,
description: Optional[str] = None
) -> model.MyResource:
"""Create a new resource."""
resource = model.MyResource(
user=trans.user,
name=name,
description=description
)
self.sa_session.add(resource)
self.sa_session.flush()
return resource
def get(self, trans: ProvidesUserContext, resource_id: int) -> model.MyResource:
"""Get resource by ID."""
resource = self.sa_session.get(model.MyResource, resource_id)
if not resource:
raise exceptions.ObjectNotFound("Resource not found")
if not self.is_accessible(resource, trans.user):
raise exceptions.ItemAccessibilityException("Access denied")
return resource
def is_accessible(self, resource: model.MyResource, user: Optional[model.User]) -> bool:
"""Check if user can access this resource."""
if not user:
return False
return resource.user_id == user.id
def list_for_user(self, trans: ProvidesUserContext) -> List[model.MyResource]:
"""List all resources for the current user."""
stmt = select(model.MyResource).where(
model.MyResource.user_id == trans.user.id
)
return self.sa_session.scalars(stmt).all()Manager best practices:
- Constructor takes (the Galaxy application object)
app - Methods take (transaction/request context) as first parameter
trans - Use for database operations
self.sa_session - Raise appropriate exceptions from
galaxy.exceptions - Implement access control checks in separate methods
- Use SQLAlchemy 2.0 syntax for queries
select()
业务逻辑应放在中的管理器类中。
lib/galaxy/managers/位置:
- 如果该领域已有管理器:更新
lib/galaxy/managers/<resource>s.py - 如果是新领域:创建
lib/galaxy/managers/<resource>s.py
管理器模式结构:
python
from typing import Optional
from galaxy import model
from galaxy.managers.context import ProvidesUserContext
from galaxy.model import Session
class MyResourceManager:
"""MyResource操作的管理器。"""
def __init__(self, app):
self.app = app
self.sa_session: Session = app.model.context
def create(
self,
trans: ProvidesUserContext,
name: str,
description: Optional[str] = None
) -> model.MyResource:
"""创建新资源。"""
resource = model.MyResource(
user=trans.user,
name=name,
description=description
)
self.sa_session.add(resource)
self.sa_session.flush()
return resource
def get(self, trans: ProvidesUserContext, resource_id: int) -> model.MyResource:
"""通过ID获取资源。"""
resource = self.sa_session.get(model.MyResource, resource_id)
if not resource:
raise exceptions.ObjectNotFound("资源未找到")
if not self.is_accessible(resource, trans.user):
raise exceptions.ItemAccessibilityException("访问被拒绝")
return resource
def is_accessible(self, resource: model.MyResource, user: Optional[model.User]) -> bool:
"""检查用户是否可以访问该资源。"""
if not user:
return False
return resource.user_id == user.id
def list_for_user(self, trans: ProvidesUserContext) -> List[model.MyResource]:
"""列出当前用户的所有资源。"""
stmt = select(model.MyResource).where(
model.MyResource.user_id == trans.user.id
)
return self.sa_session.scalars(stmt).all()管理器最佳实践:
- 构造函数接收(Galaxy应用对象)
app - 方法将(事务/请求上下文)作为第一个参数
trans - 使用进行数据库操作
self.sa_session - 抛出中的合适异常
galaxy.exceptions - 在单独的方法中实现访问控制检查
- 使用SQLAlchemy 2.0的语法进行查询
select()
Step 4: Create FastAPI Router
步骤4:创建FastAPI路由
Create or update the API router in .
lib/galaxy/webapps/galaxy/api/Location:
lib/galaxy/webapps/galaxy/api/<resource>s.pyRouter template:
python
"""
API endpoints for MyResource operations.
"""
import logging
from typing import Optional
from fastapi import (
APIRouter,
Depends,
Path,
Query,
status,
)
from galaxy.managers.context import ProvidesUserContext
from galaxy.managers.myresources import MyResourceManager
from galaxy.schema.schema import (
MyResourceCreateRequest,
MyResourceResponse,
MyResourceListResponse,
)
from galaxy.webapps.galaxy.api import (
DependsOnTrans,
Router,
)
from galaxy.webapps.galaxy.api.depends import get_app
log = logging.getLogger(__name__)
router = Router(tags=["myresources"])在中创建或更新API路由。
lib/galaxy/webapps/galaxy/api/位置:
lib/galaxy/webapps/galaxy/api/<resource>s.py路由模板:
python
"""
MyResource操作的API端点。
"""
import logging
from typing import Optional
from fastapi import (
APIRouter,
Depends,
Path,
Query,
status,
)
from galaxy.managers.context import ProvidesUserContext
from galaxy.managers.myresources import MyResourceManager
from galaxy.schema.schema import (
MyResourceCreateRequest,
MyResourceResponse,
MyResourceListResponse,
)
from galaxy.webapps.galaxy.api import (
DependsOnTrans,
Router,
)
from galaxy.webapps.galaxy.api.depends import get_app
log = logging.getLogger(__name__)
router = Router(tags=["myresources"])Dependency for manager
管理器依赖
def get_myresource_manager(app=Depends(get_app)) -> MyResourceManager:
return MyResourceManager(app)
@router.cbv
class FastAPIMyResources:
manager: MyResourceManager = Depends(get_myresource_manager)
@router.get(
"/api/myresources",
summary="List all resources for current user",
response_model=MyResourceListResponse,
)
def index(
self,
trans: ProvidesUserContext = DependsOnTrans,
) -> MyResourceListResponse:
"""List all resources owned by the current user."""
items = self.manager.list_for_user(trans)
return MyResourceListResponse(
items=[self._serialize(item) for item in items],
total_count=len(items),
)
@router.post(
"/api/myresources",
summary="Create a new resource",
status_code=status.HTTP_201_CREATED,
response_model=MyResourceResponse,
)
def create(
self,
trans: ProvidesUserContext = DependsOnTrans,
request: MyResourceCreateRequest = ...,
) -> MyResourceResponse:
"""Create a new resource."""
resource = self.manager.create(
trans,
name=request.name,
description=request.description,
)
return self._serialize(resource)
@router.get(
"/api/myresources/{id}",
summary="Get resource by ID",
response_model=MyResourceResponse,
)
def show(
self,
trans: ProvidesUserContext = DependsOnTrans,
id: EncodedDatabaseIdField = Path(..., description="Resource ID"),
) -> MyResourceResponse:
"""Get a specific resource by ID."""
decoded_id = trans.security.decode_id(id)
resource = self.manager.get(trans, decoded_id)
return self._serialize(resource)
def _serialize(self, resource) -> MyResourceResponse:
"""Convert model object to response schema."""
return MyResourceResponse(
id=trans.security.encode_id(resource.id),
name=resource.name,
description=resource.description,
create_time=resource.create_time,
update_time=resource.update_time,
)
**Router best practices:**
- Use `Router` (capital R) from `galaxy.webapps.galaxy.api` (subclass of FastAPI's APIRouter)
- Use `@router.cbv` class-based views for grouping related endpoints
- Use dependency injection for managers: `manager: Manager = Depends(get_manager)`
- Use `DependsOnTrans` for transaction context
- Path parameters use `Path(...)` with descriptions
- Query parameters use `Query(...)` with defaults
- Set appropriate HTTP status codes (`status_code=status.HTTP_201_CREATED` for creates)
- Add `summary` to all endpoints for OpenAPI docs
- Decode IDs in endpoint, not in manager (manager works with integer IDs)
---def get_myresource_manager(app=Depends(get_app)) -> MyResourceManager:
return MyResourceManager(app)
@router.cbv
class FastAPIMyResources:
manager: MyResourceManager = Depends(get_myresource_manager)
@router.get(
"/api/myresources",
summary="列出当前用户的所有资源",
response_model=MyResourceListResponse,
)
def index(
self,
trans: ProvidesUserContext = DependsOnTrans,
) -> MyResourceListResponse:
"""列出当前用户拥有的所有资源。"""
items = self.manager.list_for_user(trans)
return MyResourceListResponse(
items=[self._serialize(item) for item in items],
total_count=len(items),
)
@router.post(
"/api/myresources",
summary="创建新资源",
status_code=status.HTTP_201_CREATED,
response_model=MyResourceResponse,
)
def create(
self,
trans: ProvidesUserContext = DependsOnTrans,
request: MyResourceCreateRequest = ...,
) -> MyResourceResponse:
"""创建新资源。"""
resource = self.manager.create(
trans,
name=request.name,
description=request.description,
)
return self._serialize(resource)
@router.get(
"/api/myresources/{id}",
summary="通过ID获取资源",
response_model=MyResourceResponse,
)
def show(
self,
trans: ProvidesUserContext = DependsOnTrans,
id: EncodedDatabaseIdField = Path(..., description="资源ID"),
) -> MyResourceResponse:
"""通过ID获取特定资源。"""
decoded_id = trans.security.decode_id(id)
resource = self.manager.get(trans, decoded_id)
return self._serialize(resource)
def _serialize(self, resource) -> MyResourceResponse:
"""将模型对象转换为响应Schema。"""
return MyResourceResponse(
id=trans.security.encode_id(resource.id),
name=resource.name,
description=resource.description,
create_time=resource.create_time,
update_time=resource.update_time,
)
**路由最佳实践:**
- 使用`galaxy.webapps.galaxy.api`中的`Router`(大写R),它是FastAPI APIRouter的子类
- 使用`@router.cbv`基于类的视图来分组相关端点
- 为管理器使用依赖注入:`manager: Manager = Depends(get_manager)`
- 使用`DependsOnTrans`获取事务上下文
- 路径参数使用`Path(...)`并添加描述
- 查询参数使用`Query(...)`并设置默认值
- 设置合适的HTTP状态码(创建操作使用`status_code=status.HTTP_201_CREATED`)
- 为所有端点添加`summary`以生成OpenAPI文档
- 在端点中解码ID,而不是在管理器中(管理器使用整数ID)
---Step 5: Register Router
步骤5:注册路由
The router must be registered in the main application builder.
Location:
lib/galaxy/webapps/galaxy/buildapp.pyAdd import:
python
from galaxy.webapps.galaxy.api import myresourcesRegister router in :
app_factory()python
app.include_router(myresources.router)Find the section: Look for other calls and add yours in alphabetical order.
app.include_router()必须在主应用构建器中注册路由。
位置:
lib/galaxy/webapps/galaxy/buildapp.py添加导入:
python
from galaxy.webapps.galaxy.api import myresources在中注册路由:
app_factory()python
app.include_router(myresources.router)查找位置: 找到其他调用,并按字母顺序添加你的路由。
app.include_router()Step 6: Write API Tests
步骤6:编写API测试
Create tests in .
lib/galaxy_test/api/Location:
lib/galaxy_test/api/test_<resource>s.pyTest template:
python
"""
API tests for MyResource endpoints.
"""
from galaxy_test.base.populators import DatasetPopulator
from ._framework import ApiTestCase
class TestMyResourcesApi(ApiTestCase):
"""Tests for /api/myresources endpoints."""
def setUp(self):
super().setUp()
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)
def test_create_myresource(self):
"""Test creating a new resource."""
payload = {
"name": "Test Resource",
"description": "Test description",
}
response = self._post("myresources", data=payload, json=True)
self._assert_status_code_is(response, 201)
resource = response.json()
self._assert_has_keys(resource, "id", "name", "description", "create_time")
assert resource["name"] == "Test Resource"
def test_list_myresources(self):
"""Test listing resources."""
# Create some test data
self._create_myresource("Resource 1")
self._create_myresource("Resource 2")
# List resources
response = self._get("myresources")
self._assert_status_code_is_ok(response)
data = response.json()
assert data["total_count"] >= 2
assert len(data["items"]) >= 2
def test_get_myresource(self):
"""Test getting a specific resource."""
resource_id = self._create_myresource("Test Resource")
response = self._get(f"myresources/{resource_id}")
self._assert_status_code_is_ok(response)
resource = response.json()
assert resource["id"] == resource_id
assert resource["name"] == "Test Resource"
def test_get_nonexistent_myresource(self):
"""Test getting a resource that doesn't exist."""
response = self._get("myresources/invalid_id")
self._assert_status_code_is(response, 404)
def test_create_myresource_as_different_user(self):
"""Test that users can only see their own resources."""
# Create as first user
resource_id = self._create_myresource("User 1 Resource")
# Switch to different user
with self._different_user():
# Should not be able to access
response = self._get(f"myresources/{resource_id}")
self._assert_status_code_is(response, 403)
def _create_myresource(self, name: str) -> str:
"""Helper to create a resource and return its ID."""
payload = {"name": name, "description": f"Description for {name}"}
response = self._post("myresources", data=payload, json=True)
self._assert_status_code_is(response, 201)
return response.json()["id"]Test patterns:
- Extend from
ApiTestCaselib/galaxy_test/api/_framework.py - Use ,
self._get(),self._post(),self._put()(paths relative toself._delete())/api/ - Use for status checks
self._assert_status_code_is(response, 200) - Use for 2xx status
self._assert_status_code_is_ok(response) - Use to verify response structure
self._assert_has_keys(obj, "key1", "key2") - Use context manager to test as different user
self._different_user() - Create helper methods like for test data setup
_create_myresource() - Test both success and error cases (404, 403, 400, etc.)
在中创建测试。
lib/galaxy_test/api/位置:
lib/galaxy_test/api/test_<resource>s.py测试模板:
python
"""
MyResource端点的API测试。
"""
from galaxy_test.base.populators import DatasetPopulator
from ._framework import ApiTestCase
class TestMyResourcesApi(ApiTestCase):
"""/api/myresources端点的测试。"""
def setUp(self):
super().setUp()
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)
def test_create_myresource(self):
"""测试创建新资源。"""
payload = {
"name": "测试资源",
"description": "测试描述",
}
response = self._post("myresources", data=payload, json=True)
self._assert_status_code_is(response, 201)
resource = response.json()
self._assert_has_keys(resource, "id", "name", "description", "create_time")
assert resource["name"] == "测试资源"
def test_list_myresources(self):
"""测试列出资源。"""
# 创建一些测试数据
self._create_myresource("资源1")
self._create_myresource("资源2")
# 列出资源
response = self._get("myresources")
self._assert_status_code_is_ok(response)
data = response.json()
assert data["total_count"] >= 2
assert len(data["items"]) >= 2
def test_get_myresource(self):
"""测试获取特定资源。"""
resource_id = self._create_myresource("测试资源")
response = self._get(f"myresources/{resource_id}")
self._assert_status_code_is_ok(response)
resource = response.json()
assert resource["id"] == resource_id
assert resource["name"] == "测试资源"
def test_get_nonexistent_myresource(self):
"""测试获取不存在的资源。"""
response = self._get("myresources/invalid_id")
self._assert_status_code_is(response, 404)
def test_create_myresource_as_different_user(self):
"""测试用户只能查看自己的资源。"""
# 以第一个用户创建
resource_id = self._create_myresource("用户1的资源")
# 切换到其他用户
with self._different_user():
# 应该无法访问
response = self._get(f"myresources/{resource_id}")
self._assert_status_code_is(response, 403)
def _create_myresource(self, name: str) -> str:
"""创建资源并返回其ID的辅助方法。"""
payload = {"name": name, "description": f"{name}的描述"}
response = self._post("myresources", data=payload, json=True)
self._assert_status_code_is(response, 201)
return response.json()["id"]测试模式:
- 继承自中的
lib/galaxy_test/api/_framework.pyApiTestCase - 使用、
self._get()、self._post()、self._put()(路径相对于self._delete())/api/ - 使用检查状态码
self._assert_status_code_is(response, 200) - 使用检查2xx状态码
self._assert_status_code_is_ok(response) - 使用验证响应结构
self._assert_has_keys(obj, "key1", "key2") - 使用上下文管理器以其他用户身份测试
self._different_user() - 创建类似的辅助方法来设置测试数据
_create_myresource() - 测试成功和错误场景(404、403、400等)
Step 7: Run Tests
步骤7:运行测试
Run your new tests using the Galaxy test runner:
bash
undefined使用Galaxy测试运行器运行新测试:
bash
undefinedRun all tests for your new API
运行新API的所有测试
./run_tests.sh -api lib/galaxy_test/api/test_myresources.py
./run_tests.sh -api lib/galaxy_test/api/test_myresources.py
Run a specific test
运行特定测试
./run_tests.sh -api lib/galaxy_test/api/test_myresources.py::TestMyResourcesApi::test_create_myresource
./run_tests.sh -api lib/galaxy_test/api/test_myresources.py::TestMyResourcesApi::test_create_myresource
Run with verbose output
运行并显示详细错误
./run_tests.sh -api lib/galaxy_test/api/test_myresources.py --verbose_errors
**IMPORTANT:** Always use `./run_tests.sh`, not `pytest` directly. The wrapper script sets up the correct environment.
---./run_tests.sh -api lib/galaxy_test/api/test_myresources.py --verbose_errors
**重要提示:** 请始终使用`./run_tests.sh`,不要直接使用`pytest`。该包装脚本会设置正确的环境。
---Step 8: Verify and Manual Test
步骤8:验证和手动测试
-
Start Galaxy dev server:bash
./run.sh -
Check OpenAPI docs: Navigate toand verify your endpoints appear
http://localhost:8080/api/docs -
Manual test with curl:bash
# Create curl -X POST http://localhost:8080/api/myresources \ -H "Content-Type: application/json" \ -d '{"name": "Test", "description": "Test resource"}' # List curl http://localhost:8080/api/myresources # Get specific curl http://localhost:8080/api/myresources/{id} -
Check auto-generated TypeScript types: The frontend types inwill be auto-generated from your Pydantic schemas next time the schema is rebuilt.
client/src/api/schema/schema.ts
-
启动Galaxy开发服务器:bash
./run.sh -
检查OpenAPI文档: 导航到,验证你的端点是否已显示
http://localhost:8080/api/docs -
使用curl手动测试:bash
# 创建 curl -X POST http://localhost:8080/api/myresources \ -H "Content-Type: application/json" \ -d '{"name": "测试", "description": "测试资源"}' # 列出 curl http://localhost:8080/api/myresources # 获取特定资源 curl http://localhost:8080/api/myresources/{id} -
检查自动生成的TypeScript类型: 下次重建Schema时,中的前端类型将从你的Pydantic Schema自动生成。
client/src/api/schema/schema.ts
Reference Files to Check
参考文件
When implementing your endpoint, reference these files:
Recent API examples:
bash
ls -t lib/galaxy/webapps/galaxy/api/*.py | head -5Schema patterns:
- - Main schema definitions
lib/galaxy/schema/schema.py - - Custom field types
lib/galaxy/schema/fields.py
Manager patterns:
bash
ls lib/galaxy/managers/*.pyTest examples:
bash
ls lib/galaxy_test/api/test_*.py实现端点时,可以参考以下文件:
最新API示例:
bash
ls -t lib/galaxy/webapps/galaxy/api/*.py | head -5Schema模式:
- - 主Schema定义
lib/galaxy/schema/schema.py - - 自定义字段类型
lib/galaxy/schema/fields.py
管理器模式:
bash
ls lib/galaxy/managers/*.py测试示例:
bash
ls lib/galaxy_test/api/test_*.pyCommon Gotchas
常见陷阱
- ID encoding: Always encode IDs in API responses () and decode in endpoints
trans.security.encode_id() - Transaction context: Manager methods should take as first parameter
trans - Database session: Use after adding objects, not
self.sa_session.flush()commit() - Access control: Always check if user can access resource before returning it
- Error handling: Raise exceptions from , not generic ones
galaxy.exceptions - Router registration: Don't forget to register your router in
buildapp.py - Test runner: Use , not plain
./run_tests.sh -apipytest
- ID编码: 始终在API响应中编码ID(),在端点中解码ID
trans.security.encode_id() - 事务上下文: 管理器方法应将作为第一个参数
trans - 数据库会话: 添加对象后使用,而不是
self.sa_session.flush()commit() - 访问控制: 返回资源前始终检查用户是否有权限访问
- 错误处理: 抛出中的异常,而不是通用异常
galaxy.exceptions - 路由注册: 不要忘记在中注册路由
buildapp.py - 测试运行器: 使用,而不是直接使用
./run_tests.sh -apipytest
Next Steps
后续步骤
After creating your endpoint:
- Test thoroughly with automated tests
- Manual test through browser and curl
- Check OpenAPI documentation at
/api/docs - Consider adding frontend integration (Vue components)
- Update any relevant documentation
For more details, see:
- in this skill directory for concrete code examples
reference.md - Galaxy's CLAUDE.md for architecture overview
- Existing API implementations for patterns
创建端点后:
- 使用自动化测试进行全面测试
- 通过浏览器和curl进行手动测试
- 检查中的OpenAPI文档
/api/docs - 考虑添加前端集成(Vue组件)
- 更新所有相关文档
更多详细信息,请参阅:
- 本技能目录中的,包含具体代码示例
reference.md - Galaxy的CLAUDE.md,架构概述
- 现有API实现中的模式