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
15 changes: 15 additions & 0 deletions src/oceanum/_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
"""Base classes for oceanum models."""

from pydantic import BaseModel, ConfigDict


class StrictBaseModel(BaseModel):
"""
Base model with strict validation that forbids extra fields.

This prevents silent failures when users make typos in field names.
Instead of ignoring unknown fields, a ValidationError is raised.
"""

model_config = ConfigDict(extra="forbid")
11 changes: 6 additions & 5 deletions src/oceanum/cli/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
from typing_extensions import Self
import json

from pydantic import BaseModel, Field
from pydantic import Field
from oceanum._base import StrictBaseModel

from . import utils


class DeviceCodeResponse(BaseModel):
class DeviceCodeResponse(StrictBaseModel):
device_code: str
user_code: str
verification_uri: str
expires_in: int
interval: int
verification_uri_complete: str

class TokenResponse(BaseModel):
class TokenResponse(StrictBaseModel):
access_token: str
id_token: str|None = None
refresh_token: str|None = None
Expand Down Expand Up @@ -77,11 +78,11 @@ def delete(self) -> bool:
return True
return False

class Auth0Config(BaseModel):
class Auth0Config(StrictBaseModel):
domain: str
client_id: str

class ContextObject(BaseModel):
class ContextObject(StrictBaseModel):
domain: str
token: TokenResponse|None=None
auth0: Auth0Config|None=None
Expand Down
3 changes: 2 additions & 1 deletion src/oceanum/cli/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from tabulate import tabulate

from pydantic import BaseModel
from oceanum._base import StrictBaseModel

_sty = click.style

Expand All @@ -27,7 +28,7 @@
help='Output format'
)

class RenderField(BaseModel):
class RenderField(StrictBaseModel):
default: Any|None = None
label: str = 'Name'
path: str = '$.name'
Expand Down
8 changes: 4 additions & 4 deletions src/oceanum/datamesh/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
import warnings
from pydantic import (
ConfigDict,
BaseModel,
Field,
AnyHttpUrl,
PrivateAttr,
constr,
BeforeValidator,
field_validator,
)
from oceanum._base import StrictBaseModel
from pydantic_core import core_schema
from pydantic.json import timedelta_isoformat
from typing_extensions import Annotated
Expand Down Expand Up @@ -138,7 +138,7 @@ def __get_pydantic_json_schema__(cls, schema, handler):
]


class Schema(BaseModel):
class Schema(StrictBaseModel):
attrs: Optional[dict] = Field(title="Global attributes", default={})
dims: Optional[dict] = Field(title="Dimensions", default={})
coords: Optional[dict] = Field(title="Coordinates", default={})
Expand Down Expand Up @@ -191,7 +191,7 @@ class Coordinates(Enum):
}


class Datasource(BaseModel):
class Datasource(StrictBaseModel):
"""Datasource"""

id: str = Field(
Expand Down Expand Up @@ -325,7 +325,7 @@ class Datasource(BaseModel):
_detail: bool = PrivateAttr(default=False)
# TODO[pydantic]: The following keys were removed: `json_encoders`.
# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
model_config = ConfigDict(use_enum_values=True, validate_assignment=True)
model_config = ConfigDict(extra="forbid", use_enum_values=True, validate_assignment=True)

@field_validator("id")
@classmethod
Expand Down
29 changes: 11 additions & 18 deletions src/oceanum/datamesh/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
from pydantic import (
field_validator,
ConfigDict,
BaseModel,
Field,
BeforeValidator,
WithJsonSchema,
)
from oceanum._base import StrictBaseModel
from typing import Optional, Dict, Union, List, Any
from typing_extensions import Annotated
from enum import Enum
Expand Down Expand Up @@ -160,14 +160,14 @@ class ResampleType(str, Enum):
slinear = "linear"


class FilterGeometry(BaseModel):
class FilterGeometry(StrictBaseModel):
id: str = Field(title="Datasource ID")
parameters: Optional[Dict] = Field(
title="Optional parameters to access datasource", default={}
)


class GeoFilter(BaseModel):
class GeoFilter(StrictBaseModel):
"""GeoFilter class
Describes a spatial subset or interpolation
"""
Expand Down Expand Up @@ -225,7 +225,7 @@ def validate_geom(cls, v):
return v


class LevelFilter(BaseModel):
class LevelFilter(StrictBaseModel):
"""LevelFilter class
Describes a vertical subset or interpolation
"""
Expand Down Expand Up @@ -253,7 +253,7 @@ class LevelFilter(BaseModel):
)


class TimeFilter(BaseModel):
class TimeFilter(StrictBaseModel):
"""TimeFilter class
Describes a temporal subset or interpolation
"""
Expand Down Expand Up @@ -306,7 +306,7 @@ class AggregateOps(str, Enum):
sum = "sum"


class Aggregate(BaseModel):
class Aggregate(StrictBaseModel):
operations: List[AggregateOps] = Field(
title="Aggregate operations to perform",
default=[AggregateOps.mean],
Expand All @@ -325,7 +325,7 @@ class Aggregate(BaseModel):
)


class Function(BaseModel):
class Function(StrictBaseModel):
id: str = Field(title="Function id")
args: Dict[str, Any] = Field(title="function arguments")
vselect: Optional[List[str]] = Field(
Expand All @@ -345,14 +345,14 @@ class Function(BaseModel):
# df ∩ features -> subset of df within (resolution) of features


class CoordSelector(BaseModel):
class CoordSelector(StrictBaseModel):
coord: str = Field(title="Coordinate name")
values: List[str | int | float] = Field(
title="List of coordinate values to select by"
)


class Query(BaseModel):
class Query(StrictBaseModel):
"""
Datamesh query
"""
Expand Down Expand Up @@ -416,14 +416,7 @@ def __hash__(self):
return hash(self.model_dump_json(warnings=False))


class Workspace(BaseModel):
data: List[Query] = Field(title="Datamesh queries")
id: Optional[str] = Field(title="Unique ID of this package", default=None)
name: Optional[str] = Field(title="Package name", default="OceanQL package")
description: Optional[str] = Field(title="Package description", default="")


class Workspace(BaseModel):
class Workspace(StrictBaseModel):
data: List[Query] = Field(title="Datamesh queries")
id: Optional[str] = Field(title="Unique ID of this package", default=None)
name: Optional[str] = Field(title="Package name", default="OceanQL package")
Expand All @@ -436,7 +429,7 @@ class Container(str, Enum):
Dataset = "dataset"


class Stage(BaseModel):
class Stage(StrictBaseModel):
query: Query = Field(title="OceanQL query")
qhash: str = Field(title="Query hash")
formats: List[str] = Field(title="Available download formats")
Expand Down
4 changes: 2 additions & 2 deletions src/oceanum/datamesh/session.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel
from oceanum._base import StrictBaseModel
from typing import Optional
from datetime import datetime, timedelta
from .exceptions import DatameshConnectError, DatameshSessionError
Expand All @@ -7,7 +7,7 @@
import os


class Session(BaseModel):
class Session(StrictBaseModel):
id: str
user: str
creation_time: datetime
Expand Down
Loading
Loading