Skip to content

Commit 00efeb2

Browse files
committed
Allow for client to provide records with newer version to the server
Server might need then to either wait until upgrade it becomes capable of validating them or we might in the future to allow client to downgrade them (on client; loselessly only) since server cannot yet handle them. This behavior would facilitate testing of new releases of dandi-schema which now would fail since we require 1-to-1 correspondence. See e.g. dandi/dandi-schema#342 (comment)
1 parent a79fffc commit 00efeb2

File tree

2 files changed

+82
-39
lines changed

2 files changed

+82
-39
lines changed

dandi/dandiapi.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import click
2020
from dandischema import models
21+
from packaging.version import Version as PackagingVersion
2122
from pydantic import BaseModel, Field, PrivateAttr
2223
import requests
2324
import tenacity
@@ -646,8 +647,12 @@ def create_dandiset(
646647

647648
def check_schema_version(self, schema_version: str | None = None) -> None:
648649
"""
649-
Confirms that the server is using the same version of the DANDI schema
650-
as the client. If it is not, a `SchemaVersionError` is raised.
650+
Confirms that the server is using a "compatible" version of the DANDI schema.
651+
Compatibility here means that the server's schema version can be lower than
652+
client has, but within the same MAJOR.MINOR component of the version number
653+
for 0.x series, and same MAJOR version for/after 1.x series.
654+
655+
If it is not, a `SchemaVersionError` is raised.
651656
652657
:param schema_version: the schema version to confirm that the server
653658
uses; if not set, the schema version for the installed
@@ -662,12 +667,30 @@ def check_schema_version(self, schema_version: str | None = None) -> None:
662667
"Server did not provide schema_version in /info/;"
663668
f" returned {server_info!r}"
664669
)
665-
if server_schema_version != schema_version:
670+
server_ver, our_ver = PackagingVersion(server_schema_version), PackagingVersion(
671+
schema_version
672+
)
673+
if server_ver > our_ver:
666674
raise SchemaVersionError(
667-
f"Server requires schema version {server_schema_version};"
668-
f" client only supports {schema_version}. You may need to"
675+
f"Server uses schema version {server_schema_version};"
676+
f" client only supports prior {schema_version}. You may need to"
669677
" upgrade dandi and/or dandischema."
670678
)
679+
# NOTE: here we could in theory support older schema versions as long as they
680+
# could be migrated, but client would not know how to migrate -- server "might"
681+
# as it is the one having newer version!
682+
# TODO: check current server behavior which is likely to just not care!
683+
# So that is where server might need to provide support for upgrades upon
684+
# providing metadata.
685+
elif (
686+
server_ver.major == 0 and server_ver.release[:2] != our_ver.release[:2]
687+
) or (
688+
server_ver.major != our_ver.major
689+
): # MAJOR, MINOR within 0.x.y and MAJOR within 1.x.y
690+
raise SchemaVersionError(
691+
f"Server uses older incompatible schema version {server_schema_version};"
692+
f" client supports {schema_version}."
693+
)
671694

672695
def get_asset(self, asset_id: str) -> BaseRemoteAsset:
673696
"""

dandi/tests/test_dandiapi.py

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -272,35 +272,56 @@ def test_remote_asset_json_dict(text_dandiset: SampleDandiset) -> None:
272272
}
273273

274274

275+
@pytest.mark.parametrize(
276+
"server_schema_version,local_schema_version,should_raise,expected_message_start",
277+
[
278+
# Current/identity matching -- always ok
279+
(get_schema_version(), None, False, None),
280+
(get_schema_version(), get_schema_version(), False, None),
281+
("0.6.7", "0.6.7", False, None),
282+
# Less - is not good since we might be missing fields and it is easy
283+
# for client to upgrade.
284+
(
285+
"4.5.6",
286+
"4.5.5",
287+
True,
288+
"Server uses schema version 4.5.6; client only supports prior 4.5.5. "
289+
"You may need to upgrade dandi and/or dandischema.",
290+
),
291+
(
292+
"0.6.7",
293+
"0.6.6",
294+
True,
295+
"Server uses schema version 0.6.7; client only supports prior 0.6.6",
296+
),
297+
# Now - incompatible, for 0.x -- rely on MAJOR.MINOR
298+
(
299+
"0.6.7",
300+
"0.7.0",
301+
True,
302+
"Server uses older incompatible schema version 0.6.7; client supports 0.7.0",
303+
),
304+
# After 1.x -- rely on MAJOR.
305+
("1.0.0", "1.2.3", False, None),
306+
("1.6.7", "1.7.0", False, None),
307+
("1.6.7", "1.8.3", False, None),
308+
(
309+
"1.6.7",
310+
"2.7.0",
311+
True,
312+
"Server uses older incompatible schema version 1.6.7; client supports 2.7.0",
313+
),
314+
],
315+
)
275316
@responses.activate
276-
def test_check_schema_version_matches_default() -> None:
277-
server_info = {
278-
"schema_version": get_schema_version(),
279-
"version": "0.0.0",
280-
"services": {
281-
"api": {"url": "https://test.nil/api"},
282-
},
283-
"cli-minimal-version": "0.0.0",
284-
"cli-bad-versions": [],
285-
}
286-
responses.add(
287-
responses.GET,
288-
"https://test.nil/server-info",
289-
json=server_info,
290-
)
291-
responses.add(
292-
responses.GET,
293-
"https://test.nil/api/info/",
294-
json=server_info,
295-
)
296-
client = DandiAPIClient("https://test.nil/api")
297-
client.check_schema_version()
298-
299-
300-
@responses.activate
301-
def test_check_schema_version_mismatch() -> None:
317+
def test_check_schema_version(
318+
server_schema_version: str,
319+
local_schema_version: str | None,
320+
should_raise: bool,
321+
expected_message_start: str | None,
322+
) -> None:
302323
server_info = {
303-
"schema_version": "4.5.6",
324+
"schema_version": server_schema_version,
304325
"version": "0.0.0",
305326
"services": {
306327
"api": {"url": "https://test.nil/api"},
@@ -319,13 +340,12 @@ def test_check_schema_version_mismatch() -> None:
319340
json=server_info,
320341
)
321342
client = DandiAPIClient("https://test.nil/api")
322-
with pytest.raises(SchemaVersionError) as excinfo:
323-
client.check_schema_version("1.2.3")
324-
assert (
325-
str(excinfo.value)
326-
== "Server requires schema version 4.5.6; client only supports 1.2.3. "
327-
"You may need to upgrade dandi and/or dandischema."
328-
)
343+
if should_raise:
344+
with pytest.raises(SchemaVersionError) as excinfo:
345+
client.check_schema_version(local_schema_version)
346+
assert str(excinfo.value).startswith(expected_message_start)
347+
else:
348+
client.check_schema_version(local_schema_version)
329349

330350

331351
def test_get_dandisets(text_dandiset: SampleDandiset) -> None:

0 commit comments

Comments
 (0)