Skip to content
Open
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
42 changes: 34 additions & 8 deletions interfaces/etcd_client/interface/v0/schema.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
# Copyright 2025 Canonical
# See LICENSE file for licensing details.

from interface_tester.schema_base import DataBagSchema
from pydantic import Field
from pydantic import BaseModel, ConfigDict, Field


class ProviderSchema(DataBagSchema):
"""The schema for the provider side of this interface."""
class _BareStringDatabag(BaseModel):
"""Base class for databag models that don't strictly JSON encode all entries."""

@staticmethod
def __juju_decoder__(value: str) -> str:
"""Pass Juju's string through unmodified to be decoded by individual field validators."""
return value

@staticmethod
def __juju_encoder__(value: str | None) -> str:
"""Convert `None` to "", erasing the value; Ops will error on a non-string."""
return "" if value is None else value


class ProviderAppData(_BareStringDatabag):
"""The provider's application databag."""

model_config = ConfigDict(strict=True, populate_by_name=True)

endpoints: str = Field(
description="Comma separated list of etcd endpoints",
Expand All @@ -21,20 +36,24 @@ class ProviderSchema(DataBagSchema):
)

secret_tls: str = Field(
alias="secret-tls",
description="Secret URI containing the tls-ca",
title="TLS Secret URI",
examples=["secret://12312323112313123213"],
)

secret_user: str = Field(
alias="secret-user",
description="Secret URI containing the etcd user information",
title="User Secret URI",
examples=["secret://12312323112313123213"],
)


class RequirerSchema(DataBagSchema):
"""The schema for the requirer side of this interface."""
class RequirerAppData(_BareStringDatabag):
"""The requirer's application databag."""

model_config = ConfigDict(strict=True, populate_by_name=True)

prefix: str = Field(
description="The prefix of the range of keys requested",
Expand All @@ -43,19 +62,26 @@ class RequirerSchema(DataBagSchema):
)

secret_mtls: str = Field(
alias="secret-mtls",
description="Secret URI containing the client certificate",
title="mTLS Secret URI",
examples=["secret://12312323112313123213"],
)

requested_secrets: str = Field(
description="The fields required to be a secret.",
alias="requested-secrets",
description="The fields required to be a secret. A JSON array on the wire.",
title="Requested Secrets",
examples='["username", "uris", "tls", "tls-ca"]',
)

provided_secrets: str = Field(
description="The fields provided as secrets",
alias="provided-secrets",
description="The fields provided as secrets. A JSON array on the wire.",
title="Provided Secrets",
examples='["mtls-cert"]',
)


ProviderUnitData = None
RequirerUnitData = None
Empty file.
50 changes: 0 additions & 50 deletions interfaces/etcd_client/interface/v0/tests/test_provider.py

This file was deleted.

24 changes: 0 additions & 24 deletions interfaces/etcd_client/interface/v0/tests/test_requirer.py

This file was deleted.

7 changes: 0 additions & 7 deletions interfaces/etcd_client/ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,3 @@ quote-style = "preserve"
"D", # docs
"E501", # line too long
]
"./interface/v*/tests/*.py" = [
"CPY", # copyright
"D", # docs
"S", # security
"E501", # line too long
"F841", # assignment to unused variable
]
54 changes: 36 additions & 18 deletions interfaces/mongodb_client/interface/v0/schema.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
"""This file defines the schemas for the provider and requirer sides of the mongodb_client interface.
"""This file defines the schemas for the provider and requirer sides of the mongodb_client interface."""

It must expose two interfaces.schema_base.DataBagSchema subclasses called:
- ProviderSchema
- RequirerSchema
"""
import json
from typing import Any

from interface_tester.schema_base import DataBagSchema
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator


class MongoDBProviderData(BaseModel):
class _BareStringDatabag(BaseModel):
"""Base class for databag models that don't strictly JSON encode all entries."""

@staticmethod
def __juju_decoder__(value: str) -> str:
"""Pass Juju's string through unmodified to be decoded by individual field validators."""
return value

@staticmethod
def __juju_encoder__(value: str | None) -> str:
"""Convert `None` to "", erasing the value; Ops will error on a non-string."""
return "" if value is None else value


class ProviderAppData(_BareStringDatabag):
"""The databag for the provider side of this interface."""

model_config = ConfigDict(strict=True, populate_by_name=True)

database: str = Field(
description="The database name delivered by the provider. Might not be the same as requested by the requirer",
examples=["myapp"],
Expand Down Expand Up @@ -74,9 +87,11 @@ class MongoDBProviderData(BaseModel):
)


class MongoDBRequirerData(BaseModel):
class RequirerAppData(_BareStringDatabag):
"""The databag for the requirer side of this interface."""

model_config = ConfigDict(strict=True, populate_by_name=True)

database: str = Field(
description="The database name requested by the requirer",
examples=["myapp"],
Expand All @@ -85,7 +100,7 @@ class MongoDBRequirerData(BaseModel):

requested_secrets: list[str] = Field(
alias="requested-secrets",
description="Any provider field which should be transferred as Juju Secret",
description="Any provider field which should be transferred as Juju Secret. A JSON array on the wire.",
examples=[["username", "password"]],
title="Requested secrets",
)
Expand Down Expand Up @@ -124,14 +139,17 @@ class MongoDBRequirerData(BaseModel):
title="Entity permissions",
)

@field_validator("requested_secrets", mode="before")
@classmethod
def _load_json(cls, value: Any) -> Any:
if not isinstance(value, str):
return value # __init__ argument was already deserialized.
return json.loads(value)

class ProviderSchema(DataBagSchema):
"""The schema for the provider side of this interface."""

app: MongoDBProviderData

@field_serializer("requested_secrets")
def _dump_json(self, value: object) -> str:
return json.dumps(value)

class RequirerSchema(DataBagSchema):
"""The schema for the requirer side of this interface."""

app: MongoDBRequirerData
ProviderUnitData = None
RequirerUnitData = None
54 changes: 36 additions & 18 deletions interfaces/opensearch_client/interface/v0/schema.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
"""This file defines the schemas for the provider and requirer sides of the opensearch_client interface.
"""This file defines the schemas for the provider and requirer sides of the opensearch_client interface."""

It must expose two interfaces.schema_base.DataBagSchema subclasses called:
- ProviderSchema
- RequirerSchema
"""
import json
from typing import Any

from interface_tester.schema_base import DataBagSchema
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator


class OpenSearchProviderData(BaseModel):
class _BareStringDatabag(BaseModel):
"""Base class for databag models that don't strictly JSON encode all entries."""

@staticmethod
def __juju_decoder__(value: str) -> str:
"""Pass Juju's string through unmodified to be decoded by individual field validators."""
return value

@staticmethod
def __juju_encoder__(value: str | None) -> str:
"""Convert `None` to "", erasing the value; Ops will error on a non-string."""
return "" if value is None else value


class ProviderAppData(_BareStringDatabag):
"""The databag for the provider side of this interface."""

model_config = ConfigDict(strict=True, populate_by_name=True)

index: str = Field(
description="The index that has been made available to the relation user. Name defined in the Requirer's index field",
examples=["myindex"],
Expand Down Expand Up @@ -60,9 +73,11 @@ class OpenSearchProviderData(BaseModel):
)


class OpenSearchRequirerData(BaseModel):
class RequirerAppData(_BareStringDatabag):
"""The databag for the requirer side of this interface."""

model_config = ConfigDict(strict=True, populate_by_name=True)

index: str = Field(
description="The index name requested by the requirer",
examples=["myindex"],
Expand All @@ -71,7 +86,7 @@ class OpenSearchRequirerData(BaseModel):

requested_secrets: list[str] = Field(
alias="requested-secrets",
description="Any provider field which should be transferred as Juju Secret",
description="Any provider field which should be transferred as Juju Secret. A JSON array on the wire.",
examples=[["username", "password"]],
title="Requested secrets",
)
Expand Down Expand Up @@ -110,14 +125,17 @@ class OpenSearchRequirerData(BaseModel):
title="Entity permissions",
)

@field_validator("requested_secrets", mode="before")
@classmethod
def _load_json(cls, value: Any) -> Any:
if not isinstance(value, str):
return value # __init__ argument was already deserialized.
return json.loads(value)

class ProviderSchema(DataBagSchema):
"""The schema for the provider side of this interface."""

app: OpenSearchProviderData

@field_serializer("requested_secrets")
def _dump_json(self, value: object) -> str:
return json.dumps(value)

class RequirerSchema(DataBagSchema):
"""The schema for the requirer side of this interface."""

app: OpenSearchRequirerData
ProviderUnitData = None
RequirerUnitData = None
Loading
Loading