Skip to content

Commit da384d3

Browse files
[SILO-723] feat: Updated custom properties api to support passing options while creating (#12)
* [SILO-723] feat: Updated custom properties api to support passing options while crating * upgrade package version --------- Co-authored-by: Surya Prashanth <[email protected]>
1 parent c28a0b7 commit da384d3

File tree

5 files changed

+324
-95
lines changed

5 files changed

+324
-95
lines changed

plane/models/work_item_properties.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class WorkItemProperty(BaseModel):
4141
workspace: str | None = None
4242
project: str | None = None
4343
issue_type: str | None = None
44+
options: list["WorkItemPropertyOption"] | None = None
4445

4546
@field_serializer("property_type")
4647
def serialize_property_type(self, value: PropertyType) -> str:
@@ -68,6 +69,7 @@ class CreateWorkItemProperty(BaseModel):
6869
validation_rules: Any | None = None
6970
external_source: str | None = None
7071
external_id: str | None = None
72+
options: list["CreateWorkItemPropertyOption"] | None = None
7173

7274
@field_serializer("property_type")
7375
def serialize_property_type(self, value: PropertyType) -> str:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plane-sdk"
7-
version = "0.2.1"
7+
version = "0.2.2"
88
description = "Python SDK for Plane API"
99
readme = "README.md"
1010
requires-python = ">=3.10"

tests/scripts/test_property_values.py

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -215,66 +215,98 @@ def main() -> None:
215215
properties["datetime"] = datetime_prop
216216
print_success(f"DateTime property created: {datetime_prop.display_name}")
217217

218-
# Option property
218+
# Option property (with inline options)
219219
option_prop_data = CreateWorkItemProperty(
220220
display_name="Status",
221221
description="Current status of the task",
222222
property_type=PropertyType.OPTION.value,
223223
is_required=False,
224224
is_active=True,
225+
options=[
226+
CreateWorkItemPropertyOption(name="Not Started", description="Status: Not Started"),
227+
CreateWorkItemPropertyOption(name="In Progress", description="Status: In Progress"),
228+
CreateWorkItemPropertyOption(name="Review", description="Status: Review"),
229+
CreateWorkItemPropertyOption(name="Completed", description="Status: Completed"),
230+
CreateWorkItemPropertyOption(name="Cancelled", description="Status: Cancelled"),
231+
],
225232
)
226233
option_prop = client.work_item_properties.create(
227234
workspace_slug, project.id, task_type.id, option_prop_data
228235
)
229236
properties["option"] = option_prop
230-
print_success(f"Option property created: {option_prop.display_name}")
237+
print_success(
238+
f"Option property created: {option_prop.display_name} "
239+
f"(with {len(option_prop.options)} inline options)"
240+
)
231241

232-
# Multi-value option property for testing multi-value functionality
242+
# Multi-value option property for testing multi-value functionality (with inline options)
233243
multi_option_prop_data = CreateWorkItemProperty(
234244
display_name="Tags",
235245
description="Multiple tags for the task",
236246
property_type=PropertyType.OPTION.value,
237247
is_required=False,
238248
is_active=True,
239249
is_multi=True, # Enable multi-value support
250+
options=[
251+
CreateWorkItemPropertyOption(name="Frontend", description="Tag: Frontend"),
252+
CreateWorkItemPropertyOption(name="Backend", description="Tag: Backend"),
253+
CreateWorkItemPropertyOption(name="Database", description="Tag: Database"),
254+
CreateWorkItemPropertyOption(name="UI/UX", description="Tag: UI/UX"),
255+
CreateWorkItemPropertyOption(name="Testing", description="Tag: Testing"),
256+
CreateWorkItemPropertyOption(
257+
name="Documentation", description="Tag: Documentation"
258+
),
259+
],
240260
)
241261
multi_option_prop = client.work_item_properties.create(
242262
workspace_slug, project.id, task_type.id, multi_option_prop_data
243263
)
244264
properties["multi_option"] = multi_option_prop
245-
print_success(f"Multi-value option property created: {multi_option_prop.display_name}")
246-
247-
# Create options for the option property
248-
print_step(5, "Creating property options")
249-
status_options = []
250-
tag_options = []
251-
252-
statuses = ["Not Started", "In Progress", "Review", "Completed", "Cancelled"]
253-
for status in statuses:
254-
option_data = CreateWorkItemPropertyOption(
255-
name=status,
256-
description=f"Status: {status}",
257-
is_active=True,
258-
)
259-
option = client.work_item_properties.options.create(
260-
workspace_slug, project.id, option_prop.id, option_data
261-
)
262-
status_options.append(option)
263-
print_success(f"Status option created: {option.name}")
264-
265-
# Create options for the multi-value property
266-
tags = ["Frontend", "Backend", "Database", "UI/UX", "Testing", "Documentation"]
267-
for tag in tags:
268-
option_data = CreateWorkItemPropertyOption(
269-
name=tag,
270-
description=f"Tag: {tag}",
271-
is_active=True,
272-
)
273-
option = client.work_item_properties.options.create(
274-
workspace_slug, project.id, multi_option_prop.id, option_data
275-
)
276-
tag_options.append(option)
277-
print_success(f"Tag option created: {option.name}")
265+
print_success(
266+
f"Multi-value option property created: {multi_option_prop.display_name} "
267+
f"(with {len(multi_option_prop.options)} inline options)"
268+
)
269+
270+
# Get options from the properties (created inline)
271+
print_step(5, "Verifying inline property options")
272+
status_options = option_prop.options
273+
tag_options = multi_option_prop.options
274+
275+
print_success(f"Status options verified: {len(status_options)} options")
276+
for opt in status_options:
277+
print(f" - {opt.name}")
278+
279+
print_success(f"Tag options verified: {len(tag_options)} options")
280+
for opt in tag_options:
281+
print(f" - {opt.name}")
282+
283+
# Verify options are included in list/retrieve responses
284+
print_step(5.5, "Verifying options in list/retrieve responses")
285+
286+
# Test list response includes options
287+
all_properties = client.work_item_properties.list(workspace_slug, project.id, task_type.id)
288+
print_success(f"Listed {len(all_properties)} properties")
289+
290+
# Find option properties in list and verify options are included
291+
listed_status_prop = next((p for p in all_properties if p.id == option_prop.id), None)
292+
assert listed_status_prop is not None, "Status property should be in list"
293+
assert listed_status_prop.options is not None, "Options should be in list response"
294+
assert len(listed_status_prop.options) == 5, "Should have 5 status options in list"
295+
print_success("List response includes options for Status property ✓")
296+
297+
listed_tags_prop = next((p for p in all_properties if p.id == multi_option_prop.id), None)
298+
assert listed_tags_prop is not None, "Tags property should be in list"
299+
assert listed_tags_prop.options is not None, "Options should be in list response"
300+
assert len(listed_tags_prop.options) == 6, "Should have 6 tag options in list"
301+
print_success("List response includes options for Tags property ✓")
302+
303+
# Test retrieve response includes options
304+
retrieved_status_prop = client.work_item_properties.retrieve(
305+
workspace_slug, project.id, task_type.id, option_prop.id
306+
)
307+
assert retrieved_status_prop.options is not None, "Options should be in retrieve response"
308+
assert len(retrieved_status_prop.options) == 5, "Should have 5 options in retrieve"
309+
print_success("Retrieve response includes options ✓")
278310

279311
# Create work items for testing
280312
print_step(6, "Creating work items for property value testing")

tests/scripts/test_work_item_types_and_properties.py

Lines changed: 89 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
CreateWorkItemPropertyOption,
3030
CreateWorkItemPropertyValue,
3131
)
32+
from plane.models.work_item_property_configurations import ( # noqa: E402
33+
TextAttributeSettings,
34+
)
3235
from plane.models.work_item_types import CreateWorkItemType # noqa: E402
3336
from plane.models.work_items import CreateWorkItem # noqa: E402
3437

@@ -162,26 +165,54 @@ def main() -> None:
162165
property_type=PropertyType.TEXT.value,
163166
is_required=True,
164167
is_active=True,
168+
settings=TextAttributeSettings(display_format="single-line"),
165169
)
166170
severity_prop = client.work_item_properties.create(
167171
workspace_slug, project.id, bug_type.id, severity_prop_data
168172
)
169173
bug_properties.append(severity_prop)
170174
print_success(f"Property created: {severity_prop.display_name} (ID: {severity_prop.id})")
171175

172-
# Option property - Priority
176+
# Option property - Priority (with inline options)
173177
priority_prop_data = CreateWorkItemProperty(
174178
display_name="Priority",
175179
description="Priority level for this bug",
176180
property_type=PropertyType.OPTION.value,
177181
is_required=True,
178182
is_active=True,
183+
options=[
184+
CreateWorkItemPropertyOption(
185+
name="Critical",
186+
description="Priority level: Critical",
187+
is_active=True,
188+
is_default=False,
189+
),
190+
CreateWorkItemPropertyOption(
191+
name="High",
192+
description="Priority level: High",
193+
is_active=True,
194+
is_default=False,
195+
),
196+
CreateWorkItemPropertyOption(
197+
name="Medium",
198+
description="Priority level: Medium",
199+
is_active=True,
200+
is_default=True, # Set Medium as default
201+
),
202+
CreateWorkItemPropertyOption(
203+
name="Low",
204+
description="Priority level: Low",
205+
is_active=True,
206+
is_default=False,
207+
),
208+
],
179209
)
180210
priority_prop = client.work_item_properties.create(
181211
workspace_slug, project.id, bug_type.id, priority_prop_data
182212
)
183213
bug_properties.append(priority_prop)
184214
print_success(f"Property created: {priority_prop.display_name} (ID: {priority_prop.id})")
215+
print(f" Options created inline: {len(priority_prop.options)} options")
185216

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

214-
# Create options for the Priority property
215-
print_step(5, "Creating property options for Priority")
216-
priority_options = []
217-
218-
priority_levels = ["Critical", "High", "Medium", "Low"]
219-
for level in priority_levels:
220-
option_data = CreateWorkItemPropertyOption(
221-
name=level,
222-
description=f"Priority level: {level}",
223-
is_active=True,
224-
is_default=(level == "Medium"), # Set Medium as default
225-
)
226-
option = client.work_item_properties.options.create(
227-
workspace_slug, project.id, priority_prop.id, option_data
228-
)
229-
priority_options.append(option)
230-
print_success(f"Option created: {option.name} (ID: {option.id})")
245+
# Get options from the Priority property (created inline)
246+
print_step(5, "Verifying inline options for Priority property")
247+
priority_options = priority_prop.options
248+
print_success(f"Priority property has {len(priority_options)} options (created inline)")
249+
for option in priority_options:
250+
print(f" - {option.name} (ID: {option.id})")
251+
252+
# Verify options are included in list and retrieve responses
253+
print_step(5.5, "Verifying options in list/retrieve responses")
254+
255+
# Test list response includes options
256+
all_properties = client.work_item_properties.list(workspace_slug, project.id, bug_type.id)
257+
print_success(f"Listed {len(all_properties)} properties for Bug type")
258+
259+
# Find the priority property in the list
260+
listed_priority_prop = next((p for p in all_properties if p.id == priority_prop.id), None)
261+
assert listed_priority_prop is not None, "Priority property should be in list"
262+
assert listed_priority_prop.options is not None, "Options should be in list response"
263+
assert len(listed_priority_prop.options) == 4, "Should have 4 options in list response"
264+
print_success("List response includes options ✓")
265+
for opt in listed_priority_prop.options:
266+
print(f" - {opt.name}")
267+
268+
# Test retrieve response includes options
269+
retrieved_priority_prop = client.work_item_properties.retrieve(
270+
workspace_slug, project.id, bug_type.id, priority_prop.id
271+
)
272+
assert retrieved_priority_prop.options is not None, "Options in retrieve response"
273+
assert len(retrieved_priority_prop.options) == 4, "Should have 4 options in retrieve"
274+
print_success("Retrieve response includes options ✓")
231275

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

249293
# Set Severity (text property)
250-
severity_value_data = CreateWorkItemPropertyValue(
251-
values=[CreateWorkItemPropertyValue.ValueItem(value="High")]
252-
)
294+
severity_value_data = CreateWorkItemPropertyValue(value="High")
253295
severity_value = client.work_item_properties.values.create(
254296
workspace_slug, project.id, work_item.id, severity_prop.id, severity_value_data
255297
)
256-
print_success(f"Severity value set: {severity_value.values[0].value}")
298+
print_success(f"Severity value set: {severity_value.value}")
257299

258300
# Set Priority (option property) - use the High option
259301
high_option = next(opt for opt in priority_options if opt.name == "High")
260-
priority_value_data = CreateWorkItemPropertyValue(
261-
values=[CreateWorkItemPropertyValue.ValueItem(value=high_option.id)]
262-
)
302+
priority_value_data = CreateWorkItemPropertyValue(value=high_option.id)
263303
client.work_item_properties.values.create(
264304
workspace_slug, project.id, work_item.id, priority_prop.id, priority_value_data
265305
)
266306
print_success(f"Priority value set: {high_option.name}")
267307

268308
# Set Is Critical (boolean property)
269-
critical_value_data = CreateWorkItemPropertyValue(
270-
values=[CreateWorkItemPropertyValue.ValueItem(value=True)]
271-
)
309+
critical_value_data = CreateWorkItemPropertyValue(value=True)
272310
critical_value = client.work_item_properties.values.create(
273311
workspace_slug, project.id, work_item.id, critical_prop.id, critical_value_data
274312
)
275-
print_success(f"Critical value set: {critical_value.values[0].value}")
313+
print_success(f"Critical value set: {critical_value.value}")
276314

277315
# Set Estimated Hours (decimal property)
278-
hours_value_data = CreateWorkItemPropertyValue(
279-
values=[CreateWorkItemPropertyValue.ValueItem(value=4.5)]
280-
)
316+
hours_value_data = CreateWorkItemPropertyValue(value=4.5)
281317
hours_value = client.work_item_properties.values.create(
282318
workspace_slug, project.id, work_item.id, hours_prop.id, hours_value_data
283319
)
284-
print_success(f"Estimated hours value set: {hours_value.values[0].value}")
320+
print_success(f"Estimated hours value set: {hours_value.value}")
285321

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

291-
# Get all property values for the work item
292-
property_values = client.work_item_properties.values.list(
293-
workspace_slug, project.id, work_item.id
327+
# Retrieve individual property values to verify they were set correctly
328+
retrieved_severity = client.work_item_properties.values.retrieve(
329+
workspace_slug, project.id, work_item.id, severity_prop.id
330+
)
331+
print(f" Severity: {retrieved_severity.value}")
332+
333+
retrieved_priority = client.work_item_properties.values.retrieve(
334+
workspace_slug, project.id, work_item.id, priority_prop.id
335+
)
336+
print(f" Priority: {retrieved_priority.value}")
337+
338+
retrieved_critical = client.work_item_properties.values.retrieve(
339+
workspace_slug, project.id, work_item.id, critical_prop.id
340+
)
341+
print(f" Is Critical: {retrieved_critical.value}")
342+
343+
retrieved_hours = client.work_item_properties.values.retrieve(
344+
workspace_slug, project.id, work_item.id, hours_prop.id
294345
)
295-
print_success(f"Retrieved {len(property_values)} property values")
346+
print(f" Estimated Hours: {retrieved_hours.value}")
296347

297-
for prop_value in property_values:
298-
print(f" Property {prop_value.property_id}: {prop_value.values}")
348+
print_success("All property values retrieved successfully")
299349

300350
# Test creating a work item with Feature type
301351
print_step(9, "Creating a work item with Feature type")

0 commit comments

Comments
 (0)