Skip to main content

galaxy-api-endpoint

Create Galaxy REST API endpoints with FastAPI routers, Pydantic schemas, and manager pattern. Use for: new API routes, FastAPI endpoints, REST resources, Pydantic request/response models, lib/galaxy/webapps/galaxy/api routers, lib/galaxy/schema definitions, API controller creation.

インストールへ移動

ソース情報

リポジトリ
arash77/galaxy-claude-marketplace
ソースの最終更新活動
2026年2月11日 11:12
検出された SKILL.md の言語
英語
スター
0
フォーク
0

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
2 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
galaxy-api-endpoint
description
Create Galaxy REST API endpoints with FastAPI routers, Pydantic schemas, and manager pattern. Use for: new API routes, FastAPI endpoints, REST resources, Pydantic request/response models, lib/galaxy/webapps/galaxy/api routers, lib/galaxy/schema definitions, API controller creation.
argument-hint
[resource-name]
Persona: 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 --- ## Creating a New Galaxy API Endpoint This guide walks you through creating a new REST API endpoint following Galaxy's architecture patterns. ### Step 0: Understand the Request If $ARGUMENTS is empty, ask the user: 1. What resource are they creating an endpoint for? (e.g., "credentials", "user preferences") 2. What operation(s) are needed? (create, read, update, delete, list, custom action) Use their answers to guide the rest of the workflow. --- ## Step 1: Find Similar Endpoint as Reference Before starting, find the most recent similar endpoint to use as a pattern: ```bash # Find recently modified API routers 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 --- ## Step 2: Define Pydantic Schemas Create request/response models in `lib/galaxy/schema/` (or update existing schema file if one exists for this domain). **Location:** `lib/galaxy/schema/schema.py` (or domain-specific file like `lib/galaxy/schema/workflows.py`) **Common imports:** ```python from typing import Optional, List from pydantic import Field from galaxy.schema.fields import EncodedDatabaseIdField from galaxy.schema.schema import Model ``` **Example 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: int ``` **Field types commonly used:** - `EncodedDatabaseIdField` - For Galaxy's encoded IDs - `DecodedDatabaseIdField` - For decoded integer IDs (internal use) - `str`, `int`, `bool`, `float` - Standard types - `Optional[T]` - For nullable fields - `List[T]` - For arrays - `datetime` - For timestamps **Best practices:** - Use descriptive field names matching database column names - Add `description` to all fields for OpenAPI documentation - Use `...` for required fields, defaults for optional - Keep request models separate from response models - Use `Field(alias="...")` if API name differs from Python name --- ## Step 3: Add Manager Method 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 `app` (the Galaxy application object) - Methods take `trans` (transaction/request context) as first parameter - Use `self.sa_session` for database operations - Raise appropriate exceptions from `galaxy.exceptions` - Implement access control checks in separate methods - Use SQLAlchemy 2.0 `select()` syntax for queries --- ## Step 4: Create FastAPI Router Create or update the API router in `lib/galaxy/webapps/galaxy/api/`. **Location:** `lib/galaxy/webapps/galaxy/api/<resource>s.py` **Router 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"]) # 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) --- ## Step 5: Register Router The router must be registered in the main application builder. **Location:** `lib/galaxy/webapps/galaxy/buildapp.py` **Add import:** ```python from galaxy.webapps.galaxy.api import myresources ``` **Register router in `app_factory()`:** ```python app.include_router(myresources.router) ``` **Find the section:** Look for other `app.include_router()` calls and add yours in alphabetical order. --- ## Step 6: Write API Tests Create tests in `lib/galaxy_test/api/`. **Location:** `lib/galaxy_test/api/test_<resource>s.py` **Test 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 `ApiTestCase` from `lib/galaxy_test/api/_framework.py` - Use `self._get()`, `self._post()`, `self._put()`, `self._delete()` (paths relative to `/api/`) - Use `self._assert_status_code_is(response, 200)` for status checks - Use `self._assert_status_code_is_ok(response)` for 2xx status - Use `self._assert_has_keys(obj, "key1", "key2")` to verify response structure - Use `self._different_user()` context manager to test as different user
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る