Skip to content
Merged
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Pydantic models for EDA OpenAPI spec. Models are generated for the EDA Core API

Install dev dependencies:

```
```bash
uv sync --all-groups
```

Generate models for a specific version of the openapi repo (git ref):

```
```bash
python gen_models.py --version v25.4.1
```

Expand All @@ -31,3 +31,4 @@ The following table matches the project version with the version of the EDA deli
| pydantic_eda | EDA release |
| ------------ | ----------- |
| 0.3.2 | 25.4.1 |
| 0.4.0 | 25.8.1 |
26 changes: 22 additions & 4 deletions gen_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import argparse
import json
import logging
import os
import shutil
import subprocess
import sys
Expand Down Expand Up @@ -95,7 +96,7 @@ def process_specs(self):
api_name = "core"
# core api has a v0.0.1 in the spec but that will change
# for now use the version provided by a user from the cmd
api_version = self.version.replace(".", "_")
api_version = self.version.replace(".", "_").replace("-", "_")
logger.debug(f"API name: {api_name}, API version: {api_version}")
self.sanitize_schema_objects(spec_file, api_name, api_version)
self.generate_classes_for_spec(spec_file, api_name, api_version)
Expand Down Expand Up @@ -135,8 +136,9 @@ def generate_classes_for_spec(
"schemas",
"--output-model-type",
"pydantic_v2.BaseModel",
"--formatters",
"ruff-format",
# we will format manually using ruff in the venv
# "--formatters",
# "ruff-format",
"--use-annotated",
"--parent-scoped-naming",
"--collapse-root-models",
Expand All @@ -159,7 +161,23 @@ def generate_classes_for_spec(

try:
logger.info(f"Generating models for {spec_file}...")
subprocess.run(cmd, check=True)

# Create environment with explicit path to virtual env binaries
env = os.environ.copy()
venv_path = os.environ.get("VIRTUAL_ENV", ".venv")
venv_bin = Path(venv_path) / "bin"

# Prepend venv bin directory to PATH
current_path = env.get("PATH", "")
env["PATH"] = f"{venv_bin}:{current_path}"

subprocess.run(cmd, check=True, env=env)

# Format the generated file with ruff
logger.debug(f"Formatting {dest_file} with ruff...")
ruff_cmd = ["ruff", "format", str(dest_file)]
subprocess.run(ruff_cmd, check=True, env=env)

except subprocess.CalledProcessError as e:
logger.error(f"Error generating models for {spec_file}: {e}")

Expand Down
130 changes: 125 additions & 5 deletions pydantic_eda/apps/aaa/v1alpha1/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# filename: aaa.json

from __future__ import annotations

from typing import Annotated, Any, Dict, List, Literal, Optional
from pydantic import BaseModel, Field, RootModel

from pydantic import AwareDatetime, BaseModel, Field, RootModel


class AppGroupVersion(BaseModel):
Expand Down Expand Up @@ -37,6 +39,12 @@ class ErrorResponse(BaseModel):
description='Dictionary/map of associated data/information relevant to the error.\nThe error "message" may contain {{name}} escapes that should be substituted\nwith information from this dictionary.'
),
] = None
domain: Annotated[
Optional[str],
Field(
description='The "domain" for the error. If empty, it is an EDA\ncore error. Alternatively it can be an EDA application\n"apiVersion" value (e.g. interfaces.eda.nokia.com/v1alpha1)\nindicating that the error is specific to that application.\nThe domain gives the receiver information that they can use\nto help them interpret the "internal" error code value, or\nto find an internationalization translation for the message.'
),
] = None
errors: Annotated[
Optional[List[ErrorItem]],
Field(
Expand Down Expand Up @@ -91,7 +99,7 @@ class Resource(BaseModel):
class ResourceHistoryEntry(BaseModel):
author: Optional[str] = None
changeType: Optional[str] = None
commitTime: Optional[str] = None
commitTime: Optional[AwareDatetime] = None
hash: Optional[str] = None
message: Optional[str] = None
transactionId: Optional[int] = None
Expand All @@ -110,6 +118,43 @@ class StatusDetails(BaseModel):
name: Optional[str] = None


class TopoAttrMetadata(BaseModel):
type: Optional[str] = None
ui_description: Optional[str] = None
ui_description_key: Optional[str] = None
ui_name: Optional[str] = None
ui_name_key: Optional[str] = None


class TopoLinkEndpoint(BaseModel):
endpoint: Optional[str] = None
node: Optional[str] = None
node_key: Optional[str] = None


class TopoNodeGrouping(BaseModel):
group: Optional[str] = None
tier: Optional[int] = None


class TopoOverlayEndpointState(BaseModel):
state: Optional[int] = None


TopoOverlayLinkState = TopoOverlayEndpointState


class TopoOverlayNodeState(BaseModel):
badges: Optional[List[int]] = None
state: Optional[int] = None


class TopoSchema(BaseModel):
group: Optional[str] = None
kind: Optional[str] = None
version: Optional[str] = None


class UIResult(RootModel[str]):
root: str

Expand Down Expand Up @@ -208,7 +253,7 @@ class NodeGroupStatus(BaseModel):


class NodeGroupDeletedResourceEntry(BaseModel):
commitTime: Optional[str] = None
commitTime: Optional[AwareDatetime] = None
hash: Optional[str] = None
name: Optional[str] = None
namespace: Optional[str] = None
Expand Down Expand Up @@ -251,13 +296,78 @@ class Status(BaseModel):
string: Optional[str] = None


class TopoElemMetadata(BaseModel):
attributes: Optional[Dict[str, TopoAttrMetadata]] = None
schema_: Annotated[Optional[TopoSchema], Field(alias="schema")] = None
subtitle: Optional[str] = None
subtitle_key: Optional[str] = None


class TopoOverlayEndpoint(BaseModel):
attributes: Optional[Dict[str, Dict[str, Any]]] = None
cr_name: Optional[str] = None
labels: Optional[Dict[str, str]] = None
name: Optional[str] = None
namespace: Optional[str] = None
overlays: Optional[Dict[str, TopoOverlayEndpointState]] = None
schema_: Annotated[Optional[TopoSchema], Field(alias="schema")] = None
state: Optional[int] = None
ui_name: Optional[str] = None


class TopoOverlayLink(BaseModel):
attributes: Optional[Dict[str, Dict[str, Any]]] = None
cr_name: Optional[str] = None
endpoint_a: Optional[TopoLinkEndpoint] = None
endpoint_a_details: Optional[TopoOverlayEndpoint] = None
endpoint_b: Optional[TopoLinkEndpoint] = None
endpoint_b_details: Optional[TopoOverlayEndpoint] = None
key: Optional[str] = None
labels: Optional[Dict[str, str]] = None
name: Optional[str] = None
namespace: Optional[str] = None
overlays: Optional[Dict[str, TopoOverlayLinkState]] = None
schema_: Annotated[Optional[TopoSchema], Field(alias="schema")] = None
state: Optional[int] = None
ui_name: Optional[str] = None


class TopoOverlayNode(BaseModel):
attributes: Optional[Dict[str, Dict[str, Any]]] = None
badges: Optional[List[int]] = None
cr_name: Optional[str] = None
grouping: Optional[TopoNodeGrouping] = None
key: Optional[str] = None
labels: Optional[Dict[str, str]] = None
name: Optional[str] = None
namespace: Optional[str] = None
overlays: Optional[Dict[str, TopoOverlayNodeState]] = None
schema_: Annotated[Optional[TopoSchema], Field(alias="schema")] = None
state: Optional[int] = None
ui_name: Optional[str] = None


class Topology(BaseModel):
endpoints: Optional[TopoElemMetadata] = None
group: Optional[str] = None
grouping: Optional[TopoSchema] = None
links: Optional[TopoElemMetadata] = None
name: Optional[str] = None
nodes: Optional[TopoElemMetadata] = None
ui_description: Optional[str] = None
ui_description_key: Optional[str] = None
ui_name: Optional[str] = None
ui_name_key: Optional[str] = None
version: Optional[str] = None


class NodeGroup(BaseModel):
"""
NodeGroup is the Schema for the nodegroups API
"""

apiVersion: str
kind: str
apiVersion: Annotated[str, Field(pattern="^aaa\\.eda\\.nokia\\.com/v1alpha1$")]
kind: Annotated[str, Field(pattern="^NodeGroup$")]
metadata: NodeGroupMetadata
spec: Annotated[
NodeGroupSpec,
Expand All @@ -280,3 +390,13 @@ class NodeGroupList(BaseModel):
apiVersion: str
items: Optional[List[NodeGroup]] = None
kind: str


class OverlayState(BaseModel):
links: Optional[Dict[str, TopoOverlayLink]] = None
nodes: Optional[Dict[str, TopoOverlayNode]] = None


class ResourceTopology(BaseModel):
topology: Optional[OverlayState] = None
topologyMetadata: Optional[Topology] = None
Loading