Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow extra fields other than "@context" #266

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
62 changes: 60 additions & 2 deletions dandischema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Type,
TypeVar,
Union,
cast,
)
from warnings import warn

Expand Down Expand Up @@ -76,6 +77,51 @@ def diff_models(model1: M, model2: M) -> None:
print(f"{field} is different")


def get_dict_without_context(d: Any) -> Any:
"""
If a given object is a dictionary, return a copy of it without the
`@context` key. Otherwise, return the input object as is.

:param d: The given object
:return: If the object is a dictionary, a copy of it without the `@context` key;
otherwise, the input object as is.
"""
if isinstance(d, dict):
return {k: v for k, v in d.items() if k != "@context"}
return d


def add_context(json_schema: dict) -> None:
"""
Add the `@context` key to the given JSON schema as a required key

:param json_schema: The dictionary representing the JSON schema

raises: ValueError if the `@context` key is already present in the given schema
"""
context_key = "@context"
context_key_title = "@Context"
properties = cast(dict, json_schema.get("properties", {}))
required = cast(list, json_schema.get("required", []))

if context_key in properties or context_key in required:
msg = f"The '{context_key}' key is already present in the given JSON schema."
raise ValueError(msg)

properties[context_key] = {
"format": "uri",
"minLength": 1,
"title": context_key_title,
"type": "string",
}
required.append(context_key)
yarikoptic marked this conversation as resolved.
Show resolved Hide resolved

# Update the schema
# This is needed to handle the case in which the keys are newly created
json_schema["properties"] = properties
json_schema["required"] = required


class AccessType(Enum):
"""An enumeration of access status options"""

Expand Down Expand Up @@ -607,6 +653,8 @@ def __get_pydantic_json_schema__(

return schema

model_config = ConfigDict(extra="forbid")


class PropertyValue(DandiBaseModel):
maxValue: Optional[float] = Field(None, json_schema_extra={"nskey": "schema"})
Expand Down Expand Up @@ -1587,8 +1635,6 @@ class CommonModel(DandiBaseModel):
class Dandiset(CommonModel):
"""A body of structured information describing a DANDI dataset."""

model_config = ConfigDict(extra="allow")

@field_validator("contributor")
@classmethod
def contributor_musthave_contact(
Expand Down Expand Up @@ -1683,6 +1729,12 @@ def contributor_musthave_contact(
"nskey": "dandi",
}

# Model validator to remove the `"@context"` key from data instance before
# "base" validation is performed.
_remove_context_key = model_validator(mode="before")(get_dict_without_context)

model_config = ConfigDict(json_schema_extra=add_context)


class BareAsset(CommonModel):
"""Metadata used to describe an asset anywhere (local or server).
Expand Down Expand Up @@ -1815,6 +1867,12 @@ class Asset(BareAsset):
json_schema_extra={"readOnly": True, "nskey": "schema"}
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look here for an example on how we add extra to json schema.

May be you could add extra for your @context which would state something like "includeInUI": False


# Model validator to remove the `"@context"` key from data instance before
# "base" validation is performed.
_remove_context_key = model_validator(mode="before")(get_dict_without_context)

model_config = ConfigDict(json_schema_extra=add_context)


class Publishable(DandiBaseModel):
publishedBy: Union[AnyHttpUrl, PublishActivity] = Field(
Expand Down
Loading