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
165 changes: 165 additions & 0 deletions examples/ask_validation_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""
Example demonstrating ask type validation in werk24-python client.

This example shows how the client validates ask types before sending
requests to the API, providing helpful error messages for invalid asks.
"""

import asyncio

from werk24 import Werk24Client
from werk24.models.v1.ask import W24AskTitleBlock, W24AskVariantMeasures
from werk24.models.v2.asks import AskBalloons, AskFeatures
from werk24.utils.exceptions import BadRequestException


async def example_valid_asks():
"""Example with valid ask types (both v1 and v2)."""
print("=" * 60)
print("Example 1: Valid ask types")
print("=" * 60)

# Create a list of valid asks (mixing v1 and v2)
asks = [
W24AskTitleBlock(),
W24AskVariantMeasures(),
AskBalloons(),
AskFeatures(),
]

# Validate asks before sending (this is done automatically by read_drawing)
try:
Werk24Client.validate_asks(asks)
print("✓ All ask types are valid!")
print(f" - {len(asks)} asks validated successfully")
except BadRequestException as e:
print(f"✗ Validation failed: {e}")

print()


async def example_invalid_ask():
"""Example with an invalid ask type."""
print("=" * 60)
print("Example 2: Invalid ask type")
print("=" * 60)

from pydantic import BaseModel

# Create an invalid ask type
class InvalidAsk(BaseModel):
ask_type: str = "NONEXISTENT_ASK_TYPE"

asks = [
W24AskTitleBlock(),
InvalidAsk(), # This will fail validation
]

try:
Werk24Client.validate_asks(asks)
print("✓ All ask types are valid!")
except BadRequestException as e:
print(f"✗ Validation failed:")
print(f" {e}")

print()


async def example_empty_asks():
"""Example with empty ask list."""
print("=" * 60)
print("Example 3: Empty ask list")
print("=" * 60)

asks = []

try:
Werk24Client.validate_asks(asks)
print("✓ All ask types are valid!")
except BadRequestException as e:
print(f"✗ Validation failed:")
print(f" {e}")

print()


async def example_validation_in_read_drawing():
"""Example showing automatic validation in read_drawing."""
print("=" * 60)
print("Example 4: Automatic validation in read_drawing")
print("=" * 60)

import io

from pydantic import BaseModel

class InvalidAsk(BaseModel):
ask_type: str = "INVALID_TYPE"

client = Werk24Client()
drawing = io.BytesIO(b"fake drawing content")

print("Attempting to call read_drawing with invalid ask type...")

try:
async with client:
async for message in client.read_drawing(drawing, [InvalidAsk()]):
print(f"Received message: {message}")
except BadRequestException as e:
print(f"✓ Validation caught the error before sending to API:")
print(f" {e}")
except Exception as e:
print(f"Other error: {e}")

print()


async def example_helpful_error_message():
"""Example showing the helpful error message with valid ask types."""
print("=" * 60)
print("Example 5: Helpful error message")
print("=" * 60)

from pydantic import BaseModel

class InvalidAsk1(BaseModel):
ask_type: str = "WRONG_TYPE_1"

class InvalidAsk2(BaseModel):
ask_type: str = "WRONG_TYPE_2"

asks = [InvalidAsk1(), InvalidAsk2()]

try:
Werk24Client.validate_asks(asks)
except BadRequestException as e:
error_msg = str(e)
print("Error message includes:")
print(f" - Invalid ask types: WRONG_TYPE_1, WRONG_TYPE_2")
print(f" - List of all valid ask types")
print()
print("Full error message (truncated):")
print(f" {error_msg[:200]}...")

print()


async def main():
"""Run all examples."""
print("\n" + "=" * 60)
print("Ask Type Validation Examples")
print("=" * 60 + "\n")

await example_valid_asks()
await example_invalid_ask()
await example_empty_asks()
await example_validation_in_read_drawing()
await example_helpful_error_message()

print("=" * 60)
print("Examples completed!")
print("=" * 60)


if __name__ == "__main__":
asyncio.run(main())
42 changes: 42 additions & 0 deletions test_pydantic_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Quick test to see if Pydantic already validates ask types."""

from pydantic import BaseModel, ValidationError

from werk24.models.v1.ask import W24AskTitleBlock
from werk24.models.v2.asks import AskBalloons
from werk24.models.v2.internal import TechreadRequest

# Test 1: Valid asks should work
print("Test 1: Valid asks")
try:
request = TechreadRequest(asks=[W24AskTitleBlock(), AskBalloons()], max_pages=5)
print(f"✓ Valid asks accepted: {len(request.asks)} asks")
except ValidationError as e:
print(f"✗ Validation error: {e}")

print()

# Test 2: Invalid ask type
print("Test 2: Invalid ask type")


class InvalidAsk(BaseModel):
ask_type: str = "INVALID_TYPE"


try:
request = TechreadRequest(asks=[InvalidAsk()], max_pages=5)
print(f"✗ Invalid ask was accepted (shouldn't happen)")
except ValidationError as e:
print(f"✓ Pydantic caught the invalid ask:")
print(f" {e.errors()[0]['msg']}")

print()

# Test 3: Empty asks list
print("Test 3: Empty asks list")
try:
request = TechreadRequest(asks=[], max_pages=5)
print(f"✓ Empty asks list accepted (Pydantic doesn't validate list length)")
except ValidationError as e:
print(f"✗ Validation error: {e}")
118 changes: 118 additions & 0 deletions tests/test_ask_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
Tests for ask type validation in Werk24Client.

This module tests the validate_asks() method to ensure it properly validates
both W24AskType (v1) and AskType (v2) ask types.
"""

import pytest

from werk24 import Werk24Client
from werk24.models.v1.ask import (
W24AskTitleBlock,
W24AskVariantGDTs,
W24AskVariantMeasures,
)
from werk24.models.v2.asks import AskBalloons, AskFeatures, AskInsights
from werk24.utils.exceptions import BadRequestException


class TestAskValidation:
"""Test suite for ask type validation."""

def test_validate_asks_with_valid_v1_asks(self):
"""Test that valid v1 ask types pass validation."""
asks = [
W24AskTitleBlock(),
W24AskVariantMeasures(),
W24AskVariantGDTs(),
]
# Should not raise any exception
Werk24Client.validate_asks(asks)

def test_validate_asks_with_valid_v2_asks(self):
"""Test that valid v2 ask types pass validation."""
asks = [
AskBalloons(),
AskFeatures(),
AskInsights(),
]
# Should not raise any exception
Werk24Client.validate_asks(asks)

def test_validate_asks_with_mixed_v1_and_v2_asks(self):
"""Test that mixed v1 and v2 ask types pass validation."""
asks = [
W24AskTitleBlock(),
AskBalloons(),
W24AskVariantMeasures(),
AskFeatures(),
]
# Should not raise any exception
Werk24Client.validate_asks(asks)

def test_validate_asks_with_empty_list(self):
"""Test that empty ask list raises BadRequestException."""
with pytest.raises(BadRequestException) as exc_info:
Werk24Client.validate_asks([])

assert "No ask types provided" in str(exc_info.value)

def test_validate_asks_with_invalid_ask_type(self):
"""Test that invalid ask type raises BadRequestException with helpful message."""
from pydantic import BaseModel

class InvalidAsk(BaseModel):
ask_type: str = "INVALID_ASK_TYPE"

with pytest.raises(BadRequestException) as exc_info:
Werk24Client.validate_asks([InvalidAsk()])

error_msg = str(exc_info.value)
assert "Invalid ask type(s): INVALID_ASK_TYPE" in error_msg
assert "Valid ask types are:" in error_msg

def test_validate_asks_with_multiple_invalid_ask_types(self):
"""Test that multiple invalid ask types are all reported."""
from pydantic import BaseModel

class InvalidAsk1(BaseModel):
ask_type: str = "INVALID_TYPE_1"

class InvalidAsk2(BaseModel):
ask_type: str = "INVALID_TYPE_2"

with pytest.raises(BadRequestException) as exc_info:
Werk24Client.validate_asks([InvalidAsk1(), InvalidAsk2()])

error_msg = str(exc_info.value)
assert "INVALID_TYPE_1" in error_msg
assert "INVALID_TYPE_2" in error_msg

def test_validate_asks_with_missing_ask_type_attribute(self):
"""Test that ask without ask_type attribute raises BadRequestException."""
from pydantic import BaseModel

class AskWithoutType(BaseModel):
some_field: str = "value"

with pytest.raises(BadRequestException) as exc_info:
Werk24Client.validate_asks([AskWithoutType()])

error_msg = str(exc_info.value)
assert "(missing ask_type)" in error_msg

def test_validate_asks_error_message_includes_valid_types(self):
"""Test that error message includes list of valid ask types."""
from pydantic import BaseModel

class InvalidAsk(BaseModel):
ask_type: str = "INVALID"

with pytest.raises(BadRequestException) as exc_info:
Werk24Client.validate_asks([InvalidAsk()])

error_msg = str(exc_info.value)
# Check that some known valid types are in the error message
assert "TITLE_BLOCK" in error_msg or "BALLOONS" in error_msg
assert "VARIANT_MEASURES" in error_msg or "FEATURES" in error_msg
Loading
Loading