Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
51fd7f9
feat: have the `/info/` endpoint provide instance config info
candleindark Jun 4, 2025
70c1afb
test: update `test_rest_info()`
candleindark Jul 8, 2025
689faaa
tmp: temporarily set the dandischema dependency to the devendorize br…
candleindark Aug 21, 2025
d963767
test: provide env var to set schema config instance name
candleindark Oct 4, 2025
bfa9b08
feat: make dandischema instance config available through Django settings
candleindark Oct 4, 2025
80c99ba
feat: provide instance name through dandischema instance config
candleindark Oct 3, 2025
ca72a6d
feat: provide instance name through dandischema instance config
candleindark Oct 4, 2025
5acd73e
Merge branch 'master' into rf-jsonschema-runtime-instance-config
candleindark Oct 16, 2025
ead592c
feat: replace `"DANDI` with instance name in `DandisetList` vue compo…
candleindark Oct 8, 2025
4e6c527
ci: add DJANGO_DANDI_INSTANCE_NAME env var to frontend CI
candleindark Oct 8, 2025
4ba8be9
feat: replace hardcoded RRID with one set in schema instance config
candleindark Oct 8, 2025
e487003
ci: add instance identifier to backend CI env
candleindark Oct 8, 2025
4ccc663
feat: replace the use of hardcoded license
candleindark Oct 8, 2025
ee161a2
feat: use dandiarchive's version in producing `PublishActivity` metadata
candleindark Oct 9, 2025
72e56a5
feat: vendorize software name in `PublishActivity` metadata
candleindark Oct 9, 2025
9043b2e
test: update tests for the `PublishActivity` metadata changes
candleindark Oct 9, 2025
eb7d9d6
style: replace use of `dandiapi.__version__` with `importlib.metadata…
candleindark Oct 16, 2025
8007762
rf: remove outdated patching of JSON schema
candleindark Oct 16, 2025
421f756
Merge branch 'rf-jsonschema-runtime-instance-config' into vendor-conf…
candleindark Oct 16, 2025
064b5ad
style: replace use of `dandiapi.__version__` with `importlib.metadata…
candleindark Oct 16, 2025
fe5084e
rf: import `dandischema.conf.get_instance_config` as `get_schema_inst…
candleindark Oct 16, 2025
1c82a67
feat: call avoid using `dandiapi/settings/base.py` to store schema in…
candleindark Oct 17, 2025
1d46c87
revert: revert naming of API service in generated asset metadata
candleindark Oct 17, 2025
fb97bbd
Always use first sorted license value in create_dev_dandiset
jjnesbitt Oct 30, 2025
7fccf48
Don't rename `get_instance_config` to `get_schema_instance_config`
jjnesbitt Nov 3, 2025
ffabf98
Don't use global variables for instance config
jjnesbitt Nov 3, 2025
35b16fd
Reuse identical schema object for tests
jjnesbitt Nov 3, 2025
c8b3f10
Require DJANGO_DANDI_INSTANCE_IDENTIFIER env var
jjnesbitt Nov 3, 2025
386e705
Use dummy instance config values for local dev and CI
jjnesbitt Nov 5, 2025
f8c4dd2
Revert change of version value in PublishableMetadataMixin
jjnesbitt Nov 4, 2025
62a641d
Update tests regarding Pydantic and None values
jjnesbitt Nov 5, 2025
943fd62
Require DJANGO_DANDI_DOI_API_PREFIX env var
jjnesbitt Nov 5, 2025
173504d
Use schema vendorization for DOIs
jjnesbitt Nov 5, 2025
0a59286
Use schema vendorization for id/identifier
jjnesbitt Nov 5, 2025
7616dca
Revert unnecessary change to `$route.query`
jjnesbitt Nov 5, 2025
54b996c
Use function to supply default license value
jjnesbitt Nov 5, 2025
4ebd692
Merge branch 'master' into vendor-configurable
jjnesbitt Nov 6, 2025
aad2338
Fix linting errors
jjnesbitt Nov 6, 2025
78f180b
Fix malformed license field in create_dev_dandiset
jjnesbitt Nov 6, 2025
fa08c1e
Move `get_default_license` to conftest.py and rename
jjnesbitt Nov 6, 2025
ca9a214
Merge branch 'master' into vendor-configurable
jjnesbitt Nov 6, 2025
67ca98a
Merge branch 'master' into vendor-configurable
jjnesbitt Nov 17, 2025
155b8b9
Set dandischema dependency to 0.12.0
jjnesbitt Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ jobs:
DJANGO_DANDI_WEB_APP_URL: http://localhost:8085
DJANGO_DANDI_API_URL: http://localhost:8000
DJANGO_DANDI_JUPYTERHUB_URL: https://hub.dandiarchive.org/
DJANGO_DANDI_INSTANCE_NAME: DEV-DANDI
DJANGO_DANDI_INSTANCE_IDENTIFIER: "RRID:ABC_123456"
DJANGO_DANDI_DOI_API_PREFIX: "10.80507"
steps:
- uses: actions/checkout@v5
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ jobs:
DJANGO_DANDI_WEB_APP_URL: http://localhost:8085
DJANGO_DANDI_API_URL: http://localhost:8000
DJANGO_DANDI_JUPYTERHUB_URL: https://hub.dandiarchive.org/
DJANGO_DANDI_INSTANCE_NAME: DEV-DANDI
DJANGO_DANDI_INSTANCE_IDENTIFIER: "RRID:ABC_123456"
DJANGO_DANDI_DOI_API_PREFIX: "10.80507"

# Web client env vars
VITE_APP_DANDI_API_ROOT: http://localhost:8000/api/
Expand Down
6 changes: 4 additions & 2 deletions dandiapi/api/doi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from typing import TYPE_CHECKING

from dandischema.conf import get_instance_config
from django.conf import settings
import requests

Expand All @@ -29,10 +30,11 @@ def _generate_doi_data(version: Version):

publish = settings.DANDI_DOI_PUBLISH
# Use the DANDI test datacite instance as a placeholder if PREFIX isn't set
prefix = settings.DANDI_DOI_API_PREFIX or '10.80507'
prefix = settings.DANDI_DOI_API_PREFIX
instance_name: str = get_instance_config().instance_name
dandiset_id = version.dandiset.identifier
version_id = version.version
doi = f'{prefix}/dandi.{dandiset_id}/{version_id}'
doi = f'{prefix}/{instance_name.lower()}.{dandiset_id}/{version_id}'
metadata = version.metadata
metadata['doi'] = doi
return (doi, to_datacite(metadata, publish=publish))
Expand Down
3 changes: 2 additions & 1 deletion dandiapi/api/management/commands/create_dev_dandiset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from uuid import uuid4

from dandischema.conf import get_instance_config
from django.conf import settings
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
Expand Down Expand Up @@ -32,7 +33,7 @@ def create_dev_dandiset(*, name: str, email: str, num_extra_owners: int):

version_metadata = {
'description': 'An informative description',
'license': ['spdx:CC0-1.0'],
'license': [sorted(x.value for x in get_instance_config().licenses)[0]],
}
dandiset, draft_version = create_open_dandiset(
user=owner, version_name=name, version_metadata=version_metadata
Expand Down
8 changes: 6 additions & 2 deletions dandiapi/api/models/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
from typing import TYPE_CHECKING
from uuid import uuid4

from dandischema.conf import get_instance_config

if TYPE_CHECKING:
import datetime


class PublishableMetadataMixin:
@classmethod
def published_by(cls, now: datetime.datetime):
schema_config = get_instance_config()

return {
'id': uuid4().urn,
'name': 'DANDI publish',
Expand All @@ -20,8 +24,8 @@ def published_by(cls, now: datetime.datetime):
'wasAssociatedWith': [
{
'id': uuid4().urn,
'identifier': 'RRID:SCR_017571',
'name': 'DANDI API',
'identifier': schema_config.instance_identifier,
'name': f'{schema_config.instance_name} API',
# TODO: version the API
'version': '0.1.0',
'schemaKey': 'Software',
Expand Down
6 changes: 4 additions & 2 deletions dandiapi/api/models/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
from typing import TypedDict

from dandischema.conf import get_instance_config
from dandischema.models import AccessType
from django.conf import settings
from django.contrib.postgres.indexes import HashIndex
Expand Down Expand Up @@ -229,6 +230,7 @@ def _populate_access_metadata(self):
def _populate_metadata(self):
from dandiapi.api.manifests import manifest_location

schema_config = get_instance_config()
metadata = {
**self.metadata,
'@context': (
Expand All @@ -237,9 +239,9 @@ def _populate_metadata(self):
),
'manifestLocation': manifest_location(self),
'name': self.name,
'identifier': f'DANDI:{self.dandiset.identifier}',
'identifier': (f'{schema_config.instance_name}:{self.dandiset.identifier}'),
'version': self.version,
'id': f'DANDI:{self.dandiset.identifier}/{self.version}',
'id': (f'{schema_config.instance_name}:{self.dandiset.identifier}/{self.version}'),
'repository': settings.DANDI_WEB_APP_URL,
'url': (
f'{settings.DANDI_WEB_APP_URL}/dandiset/{self.dandiset.identifier}/{self.version}'
Expand Down
10 changes: 8 additions & 2 deletions dandiapi/api/services/metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import TYPE_CHECKING

from celery.utils.log import get_task_logger
from dandischema.conf import get_instance_config
import dandischema.exceptions
from dandischema.metadata import aggregate_assets_summary, validate
from django.conf import settings
Expand Down Expand Up @@ -108,6 +109,8 @@ def version_aggregate_assets_summary(version: Version) -> None:

def validate_version_metadata(*, version: Version) -> None:
def _build_validatable_version_metadata(version: Version) -> dict:
schema_config = get_instance_config()

# since Version.Status.VALID is a proxy for a version being publishable, we need to
# validate against the PublishedDandiset schema even though we lack several things
# at validation time: id, url, doi, and assetsSummary. this tricks the validator into
Expand All @@ -116,13 +119,16 @@ def _build_validatable_version_metadata(version: Version) -> dict:
metadata_for_validation = publishable_version.metadata

metadata_for_validation['id'] = (
f'DANDI:{publishable_version.dandiset.identifier}/{publishable_version.version}'
f'{schema_config.instance_name}:'
f'{publishable_version.dandiset.identifier}/{publishable_version.version}'
)
metadata_for_validation['url'] = (
f'{settings.DANDI_WEB_APP_URL}/dandiset/'
f'{publishable_version.dandiset.identifier}/{publishable_version.version}'
)
metadata_for_validation['doi'] = '10.80507/dandi.123456/0.123456.1234'
metadata_for_validation['doi'] = (
f'{schema_config.doi_prefix}/{schema_config.instance_name.lower()}.123456/0.123456.1234'
)
metadata_for_validation['assetsSummary'] = {
'schemaKey': 'AssetsSummary',
'numberOfBytes': 1
Expand Down
6 changes: 5 additions & 1 deletion dandiapi/api/services/publish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import datetime
from typing import TYPE_CHECKING

from dandischema.conf import get_instance_config
from dandischema.metadata import aggregate_assets_summary, validate
from django.contrib.auth.models import User
from django.db import transaction
Expand Down Expand Up @@ -182,7 +183,10 @@ def _publish_dandiset(dandiset_id: int, user_id: int) -> None:
old_version.save()

# Inject a dummy DOI so the metadata is valid
new_version.metadata['doi'] = '10.80507/dandi.123456/0.123456.1234'
schema_config = get_instance_config()
new_version.metadata['doi'] = (
f'{schema_config.doi_prefix}/{schema_config.instance_name.lower()}.123456/0.123456.1234'
)

validate(new_version.metadata, schema_key='PublishedDandiset', json_validation=True)

Expand Down
9 changes: 7 additions & 2 deletions dandiapi/api/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import hashlib

from allauth.socialaccount.models import SocialAccount
from dandischema.conf import get_instance_config
from dandischema.models import AccessType
from django.conf import settings
from django.contrib.auth.models import User
Expand Down Expand Up @@ -113,6 +114,8 @@ class Meta:

@factory.lazy_attribute
def metadata(self) -> dict:
from dandiapi.conftest import get_first_allowed_license

metadata = {
**faker.Faker().pydict(value_types=['str', 'float', 'int']),
'schemaVersion': settings.DANDI_SCHEMA_VERSION,
Expand All @@ -134,7 +137,7 @@ def metadata(self) -> dict:
'schemaKey': 'Person',
}
],
'license': ['spdx:CC0-1.0'],
'license': [get_first_allowed_license()],
}
# Remove faked data that might conflict with the schema types
for key in ['about']:
Expand Down Expand Up @@ -162,7 +165,9 @@ class DraftVersionFactory(BaseVersionFactory):

class PublishedVersionFactory(BaseVersionFactory):
doi = factory.LazyAttribute(
lambda self: f'10.80507/dandi.{self.dandiset.identifier}/{self.version}'
lambda self: f'{get_instance_config().doi_prefix}/'
f'{get_instance_config().instance_name}.'
f'{self.dandiset.identifier}/{self.version}'
)
status = Version.Status.PUBLISHED

Expand Down
14 changes: 13 additions & 1 deletion dandiapi/api/tests/fuzzy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import re

from dandischema.conf import get_instance_config


class Re:
def __init__(self, pattern):
Expand All @@ -23,12 +25,22 @@ def __hash__(self):
return hash(self.pattern)


schema_config = get_instance_config()

TIMESTAMP_RE = Re(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}Z')
UTC_ISO_TIMESTAMP_RE = Re(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}\+[0-9]{2}:[0-9]{2}')
DATE_RE = Re(r'\d{4}-\d{2}-\d{2}')
DANDISET_ID_RE = Re(r'\d{6}')
DANDISET_SCHEMA_ID_RE = Re(r'DANDI:\d{6}')
DANDISET_SCHEMA_ID_RE = Re(rf'{schema_config.instance_name}:\d{{6}}')
VERSION_ID_RE = Re(r'0\.\d{6}\.\d{4}')
HTTP_URL_RE = Re(r'http[s]?\://[^/]+(/[^/]+)*[/]?(&.+)?')
UUID_RE = Re(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
URN_RE = Re(r'urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')

DEFAULT_WAS_ASSOCIATED_WITH = {
'id': URN_RE,
'identifier': schema_config.instance_identifier,
'name': f'{schema_config.instance_name} API',
'version': '0.1.0',
'schemaKey': 'Software',
}
20 changes: 9 additions & 11 deletions dandiapi/api/tests/test_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@
from dandiapi.zarr.tasks import ingest_zarr_archive
from dandiapi.zarr.tests.factories import ZarrArchiveFactory

from .fuzzy import HTTP_URL_RE, TIMESTAMP_RE, URN_RE, UTC_ISO_TIMESTAMP_RE, UUID_RE
from .fuzzy import (
DEFAULT_WAS_ASSOCIATED_WITH,
HTTP_URL_RE,
TIMESTAMP_RE,
URN_RE,
UTC_ISO_TIMESTAMP_RE,
UUID_RE,
)

# Model tests

Expand Down Expand Up @@ -127,16 +134,7 @@ def test_publish_asset(draft_asset: Asset):
'name': 'DANDI publish',
'startDate': UTC_ISO_TIMESTAMP_RE,
'endDate': UTC_ISO_TIMESTAMP_RE,
'wasAssociatedWith': [
{
'id': URN_RE,
'identifier': 'RRID:SCR_017571',
'name': 'DANDI API',
# TODO: version the API
'version': '0.1.0',
'schemaKey': 'Software',
}
],
'wasAssociatedWith': [DEFAULT_WAS_ASSOCIATED_WITH],
'schemaKey': 'PublishActivity',
},
'datePublished': UTC_ISO_TIMESTAMP_RE,
Expand Down
Loading
Loading