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
91 changes: 91 additions & 0 deletions schemas/cache/1.0.0/deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/v1/core/deployment.json",
"title": "Deployment",
"description": "A signal deployment to a specific destination platform with activation status and key",
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"const": "platform",
"description": "Discriminator indicating this is a platform-based deployment"
},
"platform": {
"type": "string",
"description": "Platform identifier for DSPs"
},
"account": {
"type": "string",
"description": "Account identifier if applicable"
},
"is_live": {
"type": "boolean",
"description": "Whether signal is currently active on this destination"
},
"activation_key": {
"$ref": "activation-key.json",
"description": "The key to use for targeting. Only present if is_live=true AND requester has access to this destination."
},
"estimated_activation_duration_minutes": {
"type": "number",
"description": "Estimated time to activate if not live, or to complete activation if in progress",
"minimum": 0
},
"deployed_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp when activation completed (if is_live=true)"
}
},
"required": [
"type",
"platform",
"is_live"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"const": "agent",
"description": "Discriminator indicating this is an agent URL-based deployment"
},
"agent_url": {
"type": "string",
"format": "uri",
"description": "URL identifying the destination agent"
},
"account": {
"type": "string",
"description": "Account identifier if applicable"
},
"is_live": {
"type": "boolean",
"description": "Whether signal is currently active on this destination"
},
"activation_key": {
"$ref": "activation-key.json",
"description": "The key to use for targeting. Only present if is_live=true AND requester has access to this destination."
},
"estimated_activation_duration_minutes": {
"type": "number",
"description": "Estimated time to activate if not live, or to complete activation if in progress",
"minimum": 0
},
"deployed_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp when activation completed (if is_live=true)"
}
},
"required": [
"type",
"agent_url",
"is_live"
],
"additionalProperties": false
}
]
}
53 changes: 53 additions & 0 deletions schemas/cache/1.0.0/destination.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/v1/core/destination.json",
"title": "Destination",
"description": "A destination platform where signals can be activated (DSP, sales agent, etc.)",
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"const": "platform",
"description": "Discriminator indicating this is a platform-based destination"
},
"platform": {
"type": "string",
"description": "Platform identifier for DSPs (e.g., 'the-trade-desk', 'amazon-dsp')"
},
"account": {
"type": "string",
"description": "Optional account identifier on the platform"
}
},
"required": [
"type",
"platform"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"const": "agent",
"description": "Discriminator indicating this is an agent URL-based destination"
},
"agent_url": {
"type": "string",
"format": "uri",
"description": "URL identifying the destination agent (for sales agents, etc.)"
},
"account": {
"type": "string",
"description": "Optional account identifier on the agent"
}
},
"required": [
"type",
"agent_url"
],
"additionalProperties": false
}
]
}
110 changes: 110 additions & 0 deletions scripts/generate_models_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,102 @@ def escape_string_for_python(text: str) -> str:
return text.strip()


def generate_discriminated_union(schema: dict, base_name: str) -> str:
"""
Generate Pydantic models for a discriminated union (oneOf with type discriminator).

Creates a base model for each variant and a union type for the parent.
For example, Destination = PlatformDestination | AgentDestination
"""
lines = []

# Add schema description as a comment
if "description" in schema:
desc = escape_string_for_python(schema["description"])
lines.append(f"# {desc}")
lines.append("")

variant_names = []

# Generate a model for each variant in oneOf
for i, variant in enumerate(schema.get("oneOf", [])):
# Try to get discriminator value for better naming
discriminator_value = None
if "properties" in variant and "type" in variant["properties"]:
type_prop = variant["properties"]["type"]
if "const" in type_prop:
discriminator_value = type_prop["const"]

# Generate variant name
if discriminator_value:
variant_name = f"{discriminator_value.capitalize()}{base_name}"
else:
variant_name = f"{base_name}Variant{i+1}"

variant_names.append(variant_name)

# Generate the variant model
lines.append(f"class {variant_name}(BaseModel):")

# Add description if available
if "description" in variant:
desc = variant["description"].replace("\\", "\\\\").replace('"""', '\\"\\"\\"')
desc = desc.replace("\n", " ").replace("\r", "")
desc = re.sub(r"\s+", " ", desc).strip()
lines.append(f' """{desc}"""')
lines.append("")

# Add properties
if "properties" in variant and variant["properties"]:
for prop_name, prop_schema in variant["properties"].items():
safe_name, needs_alias = sanitize_field_name(prop_name)
prop_type = get_python_type(prop_schema)
desc = prop_schema.get("description", "")
if desc:
desc = escape_string_for_python(desc)

is_required = prop_name in variant.get("required", [])

if is_required:
if desc and needs_alias:
lines.append(
f' {safe_name}: {prop_type} = Field(alias="{prop_name}", description="{desc}")'
)
elif desc:
lines.append(f' {safe_name}: {prop_type} = Field(description="{desc}")')
elif needs_alias:
lines.append(f' {safe_name}: {prop_type} = Field(alias="{prop_name}")')
else:
lines.append(f" {safe_name}: {prop_type}")
else:
if desc and needs_alias:
lines.append(
f' {safe_name}: {prop_type} | None = Field(None, alias="{prop_name}", description="{desc}")'
)
elif desc:
lines.append(
f' {safe_name}: {prop_type} | None = Field(None, description="{desc}")'
)
elif needs_alias:
lines.append(
f' {safe_name}: {prop_type} | None = Field(None, alias="{prop_name}")'
)
else:
lines.append(f" {safe_name}: {prop_type} | None = None")
else:
lines.append(" pass")

lines.append("")
lines.append("")

# Create union type
union_type = " | ".join(variant_names)
lines.append(f"# Union type for {schema.get('title', base_name)}")
lines.append(f"{base_name} = {union_type}")

return "\n".join(lines)


def generate_model_for_schema(schema_file: Path) -> str:
"""Generate Pydantic model code for a single schema inline."""
with open(schema_file) as f:
Expand All @@ -74,6 +170,10 @@ def generate_model_for_schema(schema_file: Path) -> str:
# Start with model name
model_name = snake_to_pascal(schema_file.stem)

# Check if this is a oneOf discriminated union
if "oneOf" in schema and "properties" not in schema:
return generate_discriminated_union(schema, model_name)

# Check if this is a simple type alias (enum or primitive type without properties)
if "properties" not in schema:
# This is a type alias, not a model class
Expand Down Expand Up @@ -155,6 +255,13 @@ def get_python_type(schema: dict) -> str:
ref = schema["$ref"]
return snake_to_pascal(ref.replace(".json", ""))

# Handle const (discriminator values)
if "const" in schema:
const_value = schema["const"]
if isinstance(const_value, str):
return f'Literal["{const_value}"]'
return f"Literal[{const_value}]"

schema_type = schema.get("type")

if schema_type == "string":
Expand Down Expand Up @@ -387,6 +494,8 @@ def main():
"protocol-envelope.json",
"response.json",
"promoted-products.json",
"destination.json",
"deployment.json",
# Enum types (need type aliases)
"channels.json",
"delivery-type.json",
Expand Down Expand Up @@ -436,6 +545,7 @@ def main():
"",
"# These types are referenced in schemas but don't have schema files",
"# Defining them as type aliases to maintain type safety",
"ActivationKey = dict[str, Any]",
"PackageRequest = dict[str, Any]",
"PushNotificationConfig = dict[str, Any]",
"ReportingCapabilities = dict[str, Any]",
Expand Down
Loading