Skip to content

Commit ddf41c6

Browse files
yarikopticclaude
andcommitted
Add published schema endpoints and refactor to reduce code duplication
Extends the schema API to support both regular and published models: New endpoints: - /api/schema/latest/published-dandiset/ - /api/schema/<version>/published-dandiset/ - /api/schema/latest/published-asset/ - /api/schema/<version>/published-asset/ Refactoring improvements: - Extract common logic into _schema_view_impl() helper function - Update URL patterns and view exports Testing enhancements: - Extend parametrized tests to cover published schema endpoints 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 02e17e5 commit ddf41c6

File tree

4 files changed

+115
-16
lines changed

4 files changed

+115
-16
lines changed

dandiapi/api/tests/test_schema.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from dandischema.models import Asset, Dandiset
3+
from dandischema.models import Asset, Dandiset, PublishedAsset, PublishedDandiset
44
from dandischema.utils import TransitionalGenerateJsonSchema
55
from django.conf import settings
66
from django.urls import reverse
@@ -14,6 +14,13 @@
1414
('schema-dandiset-latest', Dandiset, {}),
1515
('schema-asset-latest', Asset, {}),
1616
('schema-dandiset', Dandiset, {'version': settings.DANDI_SCHEMA_VERSION}),
17+
('schema-published-dandiset-latest', PublishedDandiset, {}),
18+
('schema-published-asset-latest', PublishedAsset, {}),
19+
(
20+
'schema-published-dandiset',
21+
PublishedDandiset,
22+
{'version': settings.DANDI_SCHEMA_VERSION},
23+
),
1724
],
1825
)
1926
def test_schema_latest(api_client, endpoint, model, kwargs):
@@ -33,7 +40,13 @@ def test_schema_latest(api_client, endpoint, model, kwargs):
3340
assert schema == expected_schema
3441

3542
# Also compare against the original GitHub schema content
36-
schema_type = endpoint.split('-')[1] # Extract 'dandiset' or 'asset' from endpoint name
43+
# Extract schema type from endpoint name (e.g., 'dandiset', 'published-dandiset')
44+
endpoint_parts = endpoint.split('-')
45+
if endpoint.endswith('-latest'):
46+
schema_type = '-'.join(endpoint_parts[1:-1]) # Remove 'schema' prefix and 'latest' suffix
47+
else:
48+
schema_type = '-'.join(endpoint_parts[1:]) # Remove only 'schema' prefix
49+
3750
github_url = (
3851
'https://raw.githubusercontent.com/dandi/schema/master/'
3952
f'releases/{settings.DANDI_SCHEMA_VERSION}/{schema_type}.json'
@@ -59,6 +72,8 @@ def test_schema_latest(api_client, endpoint, model, kwargs):
5972
[
6073
'schema-dandiset',
6174
'schema-asset',
75+
'schema-published-dandiset',
76+
'schema-published-asset',
6277
],
6378
)
6479
def test_schema_invalid_version(api_client, endpoint):

dandiapi/api/views/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
from .info import info_view
88
from .robots import robots_txt_view
99
from .root import root_content_view
10-
from .schema import asset_schema_view, dandiset_schema_view
10+
from .schema import (
11+
asset_schema_view,
12+
dandiset_schema_view,
13+
published_asset_schema_view,
14+
published_dandiset_schema_view,
15+
)
1116
from .stats import stats_view
1217
from .upload import (
1318
blob_read_view,
@@ -30,6 +35,8 @@
3035
'blob_read_view',
3136
'dandiset_schema_view',
3237
'mailchimp_csv_view',
38+
'published_asset_schema_view',
39+
'published_dandiset_schema_view',
3340
'upload_initialize_view',
3441
'upload_complete_view',
3542
'upload_validate_view',

dandiapi/api/views/schema.py

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import TYPE_CHECKING, Any
44

5-
from dandischema.models import Asset, Dandiset
5+
from dandischema.models import Asset, Dandiset, PublishedAsset, PublishedDandiset
66
from dandischema.utils import TransitionalGenerateJsonSchema
77
from django.conf import settings
88
from django.http import HttpRequest, JsonResponse
@@ -31,6 +31,31 @@ def generate_model_schema(model_class: type[BaseModel]) -> dict[str, Any]:
3131
return model_class.model_json_schema(schema_generator=TransitionalGenerateJsonSchema)
3232

3333

34+
def _schema_view_impl(
35+
request: HttpRequest,
36+
model_class: type[BaseModel],
37+
version: str | None = None,
38+
) -> JsonResponse:
39+
"""
40+
Implement generic schema endpoint logic.
41+
42+
Args:
43+
request: HTTP request object
44+
model_class: Pydantic model class to generate schema for
45+
version: Schema version (optional)
46+
47+
Returns:
48+
JsonResponse containing the JSON schema
49+
"""
50+
if version and version not in {settings.DANDI_SCHEMA_VERSION, 'latest'}:
51+
raise NotFound(f'Schema version {version} not found')
52+
53+
# Generate the schema JSON using the same approach as dandischema
54+
schema = generate_model_schema(model_class)
55+
56+
return JsonResponse(schema)
57+
58+
3459
@swagger_auto_schema(
3560
method='GET',
3661
operation_summary='Get schema for Dandiset',
@@ -48,13 +73,7 @@ def dandiset_schema_view(request: HttpRequest, version: str | None = None) -> Js
4873
If a version is provided and does not match the current version, a 404 is returned.
4974
In the future, multiple versions could be supported.
5075
"""
51-
if version and version not in {settings.DANDI_SCHEMA_VERSION, 'latest'}:
52-
raise NotFound(f'Schema version {version} not found')
53-
54-
# Generate the schema JSON using the same approach as dandischema
55-
schema = generate_model_schema(Dandiset)
56-
57-
return JsonResponse(schema)
76+
return _schema_view_impl(request, Dandiset, version)
5877

5978

6079
@swagger_auto_schema(
@@ -74,10 +93,46 @@ def asset_schema_view(request: HttpRequest, version: str | None = None) -> JsonR
7493
If a version is provided and does not match the current version, a 404 is returned.
7594
In the future, multiple versions could be supported.
7695
"""
77-
if version and version not in {settings.DANDI_SCHEMA_VERSION, 'latest'}:
78-
raise NotFound(f'Schema version {version} not found')
96+
return _schema_view_impl(request, Asset, version)
7997

80-
# Generate the schema JSON using the same approach as dandischema
81-
schema = generate_model_schema(Asset)
8298

83-
return JsonResponse(schema)
99+
@swagger_auto_schema(
100+
method='GET',
101+
operation_summary='Get schema for Published Dandiset',
102+
operation_description='Returns the JSONSchema for Published Dandiset metadata',
103+
)
104+
@api_view(['GET'])
105+
def published_dandiset_schema_view(
106+
request: HttpRequest, version: str | None = None
107+
) -> JsonResponse:
108+
"""
109+
Return the JSONSchema for Published Dandiset metadata.
110+
111+
This endpoint provides the currently configured schema based on the application's
112+
DANDI_SCHEMA_VERSION setting. Published Dandisets have additional required fields
113+
and constraints compared to draft Dandisets.
114+
115+
If a version is provided and does not match the current version, a 404 is returned.
116+
In the future, multiple versions could be supported.
117+
"""
118+
return _schema_view_impl(request, PublishedDandiset, version)
119+
120+
121+
@swagger_auto_schema(
122+
method='GET',
123+
operation_summary='Get schema for Published Asset',
124+
operation_description='Returns the JSONSchema for Published Asset metadata',
125+
)
126+
@api_view(['GET'])
127+
def published_asset_schema_view(request: HttpRequest, version: str | None = None) -> JsonResponse:
128+
"""
129+
Return the JSONSchema for Published Asset metadata.
130+
131+
This endpoint provides the currently configured schema based on the application's
132+
DANDI_SCHEMA_VERSION setting. Published Assets have additional required fields
133+
and constraints compared to draft Assets.
134+
135+
If a version is provided and does not match the current version, a 404 is returned.
136+
In the future, multiple versions could be supported.
137+
"""
138+
return _schema_view_impl(request, PublishedAsset, version)

dandiapi/urls.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
dandiset_schema_view,
2323
info_view,
2424
mailchimp_csv_view,
25+
published_asset_schema_view,
26+
published_dandiset_schema_view,
2527
robots_txt_view,
2628
root_content_view,
2729
stats_view,
@@ -70,6 +72,26 @@
7072
path('api/schema/<str:version>/dandiset/', dandiset_schema_view, name='schema-dandiset'),
7173
path('api/schema/latest/asset/', asset_schema_view, name='schema-asset-latest'),
7274
path('api/schema/<str:version>/asset/', asset_schema_view, name='schema-asset'),
75+
path(
76+
'api/schema/latest/published-dandiset/',
77+
published_dandiset_schema_view,
78+
name='schema-published-dandiset-latest',
79+
),
80+
path(
81+
'api/schema/<str:version>/published-dandiset/',
82+
published_dandiset_schema_view,
83+
name='schema-published-dandiset',
84+
),
85+
path(
86+
'api/schema/latest/published-asset/',
87+
published_asset_schema_view,
88+
name='schema-published-asset-latest',
89+
),
90+
path(
91+
'api/schema/<str:version>/published-asset/',
92+
published_asset_schema_view,
93+
name='schema-published-asset',
94+
),
7395
path('api/uploads/initialize/', upload_initialize_view, name='upload-initialize'),
7496
re_path(
7597
r'api/uploads/(?P<upload_id>[0-9a-f\-]{36})/complete/',

0 commit comments

Comments
 (0)