Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions src/datamodel_code_generator/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ def add_id(self, id_: str, path: Sequence[str]) -> None:
"""Register an identifier mapping to a resolved reference path."""
self.ids["/".join(self.current_root)][id_] = self.resolve_ref(path)

def resolve_ref(self, path: Sequence[str] | str) -> str: # noqa: PLR0911, PLR0912, PLR0914
def resolve_ref(self, path: Sequence[str] | str) -> str: # noqa: PLR0911, PLR0912, PLR0914, PLR0915
"""Resolve a reference path to its canonical form."""
joined_path = path if isinstance(path, str) else self.join_path(tuple(path))
if joined_path == "#":
Expand Down Expand Up @@ -679,7 +679,9 @@ def resolve_ref(self, path: Sequence[str] | str) -> str: # noqa: PLR0911, PLR09
if self.base_url:
from .http import join_url # noqa: PLC0415

effective_base = self.root_id or self.base_url
effective_base = self.base_url
if self.root_id:
effective_base = self.root_id if is_url(self.root_id) else join_url(self.base_url, self.root_id)
joined_url = join_url(effective_base, ref)
if "#" in joined_url:
return joined_url
Expand Down
67 changes: 67 additions & 0 deletions tests/main/jsonschema/test_main_jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,73 @@ def test_main_root_id_jsonschema_with_absolute_local_file(output_file: Path) ->
)


def test_main_url_with_relative_root_id_resolves_relative_refs(mocker: MockerFixture, tmp_path: Path) -> None:
"""Test --url input keeps resolving relative refs remotely when root $id is path-only."""
main_response = mocker.Mock()
main_response.status_code = 200
main_response.headers = {}
main_response.text = json.dumps({
"$id": "/schemas/v1/main.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Main",
"type": "object",
"properties": {
"sub": {
"$ref": "sub.schema.json",
}
},
"required": ["sub"],
})
sub_response = mocker.Mock()
sub_response.status_code = 200
sub_response.headers = {}
sub_response.text = json.dumps({
"$id": "/schemas/v1/sub.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Sub",
"type": "string",
"pattern": "^[0-9a-f]{8}$",
})
httpx_get_mock = mocker.patch("httpx.get", side_effect=[main_response, sub_response])
output_dir = tmp_path / "output"

result = run_main_with_args([
"--url",
"http://localhost:8888/schemas/v1/main.schema.json",
"--output",
str(output_dir),
"--input-file-type",
"jsonschema",
"--output-model-type",
"pydantic_v2.BaseModel",
])

assert result == Exit.OK
main_content = (output_dir / "__init__.py").read_text(encoding="utf-8")
sub_content = (output_dir / "sub.py").read_text(encoding="utf-8")
assert "class Main(BaseModel):" in main_content
assert "sub: sub_1.Schema" in main_content
assert "class Schema(RootModel[constr(pattern=r'^[0-9a-f]{8}$')]):" in sub_content
httpx_get_mock.assert_has_calls([
call(
"http://localhost:8888/schemas/v1/main.schema.json",
headers=None,
verify=True,
follow_redirects=True,
params=None,
timeout=30.0,
),
call(
"http://localhost:8888/schemas/v1/sub.schema.json",
headers=None,
verify=True,
follow_redirects=True,
params=None,
timeout=30.0,
),
])


def test_main_remote_ref_emits_deprecation_warning(mocker: MockerFixture, tmp_path: Path) -> None:
"""Test that implicit remote $ref fetching emits a FutureWarning when flag is not set."""
person_response = mocker.Mock()
Expand Down
10 changes: 10 additions & 0 deletions tests/test_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ def test_resolve_ref_with_root_id_differs_from_base_url() -> None:
assert result == "https://example.com/common/types.json#"


def test_resolve_ref_with_relative_root_id_and_base_url() -> None:
"""Relative root $id should be resolved against the retrieval URL before $ref resolution."""
resolver = ModelResolver(base_url="http://localhost:8888/schemas/v1/main.schema.json")
resolver.set_root_id("/schemas/v1/main.schema.json")

result = resolver.resolve_ref("sub.schema.json")

assert result == "http://localhost:8888/schemas/v1/sub.schema.json#"


@pytest.mark.parametrize(
("base_url", "ref", "expected"),
[
Expand Down
Loading