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
2 changes: 2 additions & 0 deletions plane/models/work_item_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class WorkItemProperty(BaseModel):
workspace: str | None = None
project: str | None = None
issue_type: str | None = None
options: list["WorkItemPropertyOption"] | None = None

@field_serializer("property_type")
def serialize_property_type(self, value: PropertyType) -> str:
Expand Down Expand Up @@ -68,6 +69,7 @@ class CreateWorkItemProperty(BaseModel):
validation_rules: Any | None = None
external_source: str | None = None
external_id: str | None = None
options: list["CreateWorkItemPropertyOption"] | None = None

@field_serializer("property_type")
def serialize_property_type(self, value: PropertyType) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "plane-sdk"
version = "0.2.1"
version = "0.2.2"
description = "Python SDK for Plane API"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
104 changes: 68 additions & 36 deletions tests/scripts/test_property_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,66 +215,98 @@ def main() -> None:
properties["datetime"] = datetime_prop
print_success(f"DateTime property created: {datetime_prop.display_name}")

# Option property
# Option property (with inline options)
option_prop_data = CreateWorkItemProperty(
display_name="Status",
description="Current status of the task",
property_type=PropertyType.OPTION.value,
is_required=False,
is_active=True,
options=[
CreateWorkItemPropertyOption(name="Not Started", description="Status: Not Started"),
CreateWorkItemPropertyOption(name="In Progress", description="Status: In Progress"),
CreateWorkItemPropertyOption(name="Review", description="Status: Review"),
CreateWorkItemPropertyOption(name="Completed", description="Status: Completed"),
CreateWorkItemPropertyOption(name="Cancelled", description="Status: Cancelled"),
],
)
option_prop = client.work_item_properties.create(
workspace_slug, project.id, task_type.id, option_prop_data
)
properties["option"] = option_prop
print_success(f"Option property created: {option_prop.display_name}")
print_success(
f"Option property created: {option_prop.display_name} "
f"(with {len(option_prop.options)} inline options)"
)

# Multi-value option property for testing multi-value functionality
# Multi-value option property for testing multi-value functionality (with inline options)
multi_option_prop_data = CreateWorkItemProperty(
display_name="Tags",
description="Multiple tags for the task",
property_type=PropertyType.OPTION.value,
is_required=False,
is_active=True,
is_multi=True, # Enable multi-value support
options=[
CreateWorkItemPropertyOption(name="Frontend", description="Tag: Frontend"),
CreateWorkItemPropertyOption(name="Backend", description="Tag: Backend"),
CreateWorkItemPropertyOption(name="Database", description="Tag: Database"),
CreateWorkItemPropertyOption(name="UI/UX", description="Tag: UI/UX"),
CreateWorkItemPropertyOption(name="Testing", description="Tag: Testing"),
CreateWorkItemPropertyOption(
name="Documentation", description="Tag: Documentation"
),
],
)
multi_option_prop = client.work_item_properties.create(
workspace_slug, project.id, task_type.id, multi_option_prop_data
)
properties["multi_option"] = multi_option_prop
print_success(f"Multi-value option property created: {multi_option_prop.display_name}")

# Create options for the option property
print_step(5, "Creating property options")
status_options = []
tag_options = []

statuses = ["Not Started", "In Progress", "Review", "Completed", "Cancelled"]
for status in statuses:
option_data = CreateWorkItemPropertyOption(
name=status,
description=f"Status: {status}",
is_active=True,
)
option = client.work_item_properties.options.create(
workspace_slug, project.id, option_prop.id, option_data
)
status_options.append(option)
print_success(f"Status option created: {option.name}")

# Create options for the multi-value property
tags = ["Frontend", "Backend", "Database", "UI/UX", "Testing", "Documentation"]
for tag in tags:
option_data = CreateWorkItemPropertyOption(
name=tag,
description=f"Tag: {tag}",
is_active=True,
)
option = client.work_item_properties.options.create(
workspace_slug, project.id, multi_option_prop.id, option_data
)
tag_options.append(option)
print_success(f"Tag option created: {option.name}")
print_success(
f"Multi-value option property created: {multi_option_prop.display_name} "
f"(with {len(multi_option_prop.options)} inline options)"
)

# Get options from the properties (created inline)
print_step(5, "Verifying inline property options")
status_options = option_prop.options
tag_options = multi_option_prop.options

print_success(f"Status options verified: {len(status_options)} options")
for opt in status_options:
print(f" - {opt.name}")

print_success(f"Tag options verified: {len(tag_options)} options")
for opt in tag_options:
print(f" - {opt.name}")

# Verify options are included in list/retrieve responses
print_step(5.5, "Verifying options in list/retrieve responses")

# Test list response includes options
all_properties = client.work_item_properties.list(workspace_slug, project.id, task_type.id)
print_success(f"Listed {len(all_properties)} properties")

# Find option properties in list and verify options are included
listed_status_prop = next((p for p in all_properties if p.id == option_prop.id), None)
assert listed_status_prop is not None, "Status property should be in list"
assert listed_status_prop.options is not None, "Options should be in list response"
assert len(listed_status_prop.options) == 5, "Should have 5 status options in list"
print_success("List response includes options for Status property ✓")

listed_tags_prop = next((p for p in all_properties if p.id == multi_option_prop.id), None)
assert listed_tags_prop is not None, "Tags property should be in list"
assert listed_tags_prop.options is not None, "Options should be in list response"
assert len(listed_tags_prop.options) == 6, "Should have 6 tag options in list"
print_success("List response includes options for Tags property ✓")

# Test retrieve response includes options
retrieved_status_prop = client.work_item_properties.retrieve(
workspace_slug, project.id, task_type.id, option_prop.id
)
assert retrieved_status_prop.options is not None, "Options should be in retrieve response"
assert len(retrieved_status_prop.options) == 5, "Should have 5 options in retrieve"
print_success("Retrieve response includes options ✓")

# Create work items for testing
print_step(6, "Creating work items for property value testing")
Expand Down
128 changes: 89 additions & 39 deletions tests/scripts/test_work_item_types_and_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
CreateWorkItemPropertyOption,
CreateWorkItemPropertyValue,
)
from plane.models.work_item_property_configurations import ( # noqa: E402
TextAttributeSettings,
)
from plane.models.work_item_types import CreateWorkItemType # noqa: E402
from plane.models.work_items import CreateWorkItem # noqa: E402

Expand Down Expand Up @@ -162,26 +165,54 @@ def main() -> None:
property_type=PropertyType.TEXT.value,
is_required=True,
is_active=True,
settings=TextAttributeSettings(display_format="single-line"),
)
severity_prop = client.work_item_properties.create(
workspace_slug, project.id, bug_type.id, severity_prop_data
)
bug_properties.append(severity_prop)
print_success(f"Property created: {severity_prop.display_name} (ID: {severity_prop.id})")

# Option property - Priority
# Option property - Priority (with inline options)
priority_prop_data = CreateWorkItemProperty(
display_name="Priority",
description="Priority level for this bug",
property_type=PropertyType.OPTION.value,
is_required=True,
is_active=True,
options=[
CreateWorkItemPropertyOption(
name="Critical",
description="Priority level: Critical",
is_active=True,
is_default=False,
),
CreateWorkItemPropertyOption(
name="High",
description="Priority level: High",
is_active=True,
is_default=False,
),
CreateWorkItemPropertyOption(
name="Medium",
description="Priority level: Medium",
is_active=True,
is_default=True, # Set Medium as default
),
CreateWorkItemPropertyOption(
name="Low",
description="Priority level: Low",
is_active=True,
is_default=False,
),
],
)
priority_prop = client.work_item_properties.create(
workspace_slug, project.id, bug_type.id, priority_prop_data
)
bug_properties.append(priority_prop)
print_success(f"Property created: {priority_prop.display_name} (ID: {priority_prop.id})")
print(f" Options created inline: {len(priority_prop.options)} options")

# Boolean property - Is Critical
critical_prop_data = CreateWorkItemProperty(
Expand Down Expand Up @@ -211,23 +242,36 @@ def main() -> None:
bug_properties.append(hours_prop)
print_success(f"Property created: {hours_prop.display_name} (ID: {hours_prop.id})")

# Create options for the Priority property
print_step(5, "Creating property options for Priority")
priority_options = []

priority_levels = ["Critical", "High", "Medium", "Low"]
for level in priority_levels:
option_data = CreateWorkItemPropertyOption(
name=level,
description=f"Priority level: {level}",
is_active=True,
is_default=(level == "Medium"), # Set Medium as default
)
option = client.work_item_properties.options.create(
workspace_slug, project.id, priority_prop.id, option_data
)
priority_options.append(option)
print_success(f"Option created: {option.name} (ID: {option.id})")
# Get options from the Priority property (created inline)
print_step(5, "Verifying inline options for Priority property")
priority_options = priority_prop.options
print_success(f"Priority property has {len(priority_options)} options (created inline)")
for option in priority_options:
print(f" - {option.name} (ID: {option.id})")

# Verify options are included in list and retrieve responses
print_step(5.5, "Verifying options in list/retrieve responses")

# Test list response includes options
all_properties = client.work_item_properties.list(workspace_slug, project.id, bug_type.id)
print_success(f"Listed {len(all_properties)} properties for Bug type")

# Find the priority property in the list
listed_priority_prop = next((p for p in all_properties if p.id == priority_prop.id), None)
assert listed_priority_prop is not None, "Priority property should be in list"
assert listed_priority_prop.options is not None, "Options should be in list response"
assert len(listed_priority_prop.options) == 4, "Should have 4 options in list response"
print_success("List response includes options ✓")
for opt in listed_priority_prop.options:
print(f" - {opt.name}")

# Test retrieve response includes options
retrieved_priority_prop = client.work_item_properties.retrieve(
workspace_slug, project.id, bug_type.id, priority_prop.id
)
assert retrieved_priority_prop.options is not None, "Options in retrieve response"
assert len(retrieved_priority_prop.options) == 4, "Should have 4 options in retrieve"
print_success("Retrieve response includes options ✓")

# Create a work item with the Bug type
print_step(6, "Creating a work item with Bug type and custom properties")
Expand All @@ -247,55 +291,61 @@ def main() -> None:
print_step(7, "Assigning custom property values to work item")

# Set Severity (text property)
severity_value_data = CreateWorkItemPropertyValue(
values=[CreateWorkItemPropertyValue.ValueItem(value="High")]
)
severity_value_data = CreateWorkItemPropertyValue(value="High")
severity_value = client.work_item_properties.values.create(
workspace_slug, project.id, work_item.id, severity_prop.id, severity_value_data
)
print_success(f"Severity value set: {severity_value.values[0].value}")
print_success(f"Severity value set: {severity_value.value}")

# Set Priority (option property) - use the High option
high_option = next(opt for opt in priority_options if opt.name == "High")
priority_value_data = CreateWorkItemPropertyValue(
values=[CreateWorkItemPropertyValue.ValueItem(value=high_option.id)]
)
priority_value_data = CreateWorkItemPropertyValue(value=high_option.id)
client.work_item_properties.values.create(
workspace_slug, project.id, work_item.id, priority_prop.id, priority_value_data
)
print_success(f"Priority value set: {high_option.name}")

# Set Is Critical (boolean property)
critical_value_data = CreateWorkItemPropertyValue(
values=[CreateWorkItemPropertyValue.ValueItem(value=True)]
)
critical_value_data = CreateWorkItemPropertyValue(value=True)
critical_value = client.work_item_properties.values.create(
workspace_slug, project.id, work_item.id, critical_prop.id, critical_value_data
)
print_success(f"Critical value set: {critical_value.values[0].value}")
print_success(f"Critical value set: {critical_value.value}")

# Set Estimated Hours (decimal property)
hours_value_data = CreateWorkItemPropertyValue(
values=[CreateWorkItemPropertyValue.ValueItem(value=4.5)]
)
hours_value_data = CreateWorkItemPropertyValue(value=4.5)
hours_value = client.work_item_properties.values.create(
workspace_slug, project.id, work_item.id, hours_prop.id, hours_value_data
)
print_success(f"Estimated hours value set: {hours_value.values[0].value}")
print_success(f"Estimated hours value set: {hours_value.value}")

# Retrieve and verify the work item with its property values
print_step(8, "Verifying work item and property values")
retrieved_work_item = client.work_items.retrieve(workspace_slug, project.id, work_item.id)
print_success(f"Work item retrieved: {retrieved_work_item.name}")

# Get all property values for the work item
property_values = client.work_item_properties.values.list(
workspace_slug, project.id, work_item.id
# Retrieve individual property values to verify they were set correctly
retrieved_severity = client.work_item_properties.values.retrieve(
workspace_slug, project.id, work_item.id, severity_prop.id
)
print(f" Severity: {retrieved_severity.value}")

retrieved_priority = client.work_item_properties.values.retrieve(
workspace_slug, project.id, work_item.id, priority_prop.id
)
print(f" Priority: {retrieved_priority.value}")

retrieved_critical = client.work_item_properties.values.retrieve(
workspace_slug, project.id, work_item.id, critical_prop.id
)
print(f" Is Critical: {retrieved_critical.value}")

retrieved_hours = client.work_item_properties.values.retrieve(
workspace_slug, project.id, work_item.id, hours_prop.id
)
print_success(f"Retrieved {len(property_values)} property values")
print(f" Estimated Hours: {retrieved_hours.value}")

for prop_value in property_values:
print(f" Property {prop_value.property_id}: {prop_value.values}")
print_success("All property values retrieved successfully")

# Test creating a work item with Feature type
print_step(9, "Creating a work item with Feature type")
Expand Down
Loading