glean-indexing-sdk 0.0.3__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.cz.toml +1 -1
  2. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/PKG-INFO +1 -1
  3. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/pyproject.toml +1 -1
  4. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/__init__.py +1 -1
  5. glean_indexing_sdk-0.1.0/src/glean/indexing/common/property_definition_builder.py +115 -0
  6. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/taskfile.yml +2 -0
  7. glean_indexing_sdk-0.1.0/tests/unit_tests/common/test_property_definition_builder.py +246 -0
  8. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_base_people_connector.py +1 -1
  9. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_custom_connector_integration.py +1 -1
  10. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.env.template +0 -0
  11. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.github/CODEOWNERS +0 -0
  12. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.github/workflows/ci.yml +0 -0
  13. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.github/workflows/publish.yml +0 -0
  14. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.gitignore +0 -0
  15. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.markdown-coderc.json +0 -0
  16. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.python-version +0 -0
  17. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.ruff.toml +0 -0
  18. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/.vscode/settings.json +0 -0
  19. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/CHANGELOG.md +0 -0
  20. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/CONTRIBUTING.md +0 -0
  21. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/LICENSE +0 -0
  22. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/README.md +0 -0
  23. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/RELEASE.md +0 -0
  24. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/env.template +0 -0
  25. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/mise.toml +0 -0
  26. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/non_streaming/complete.py +0 -0
  27. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/non_streaming/run_connector.py +0 -0
  28. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/non_streaming/wiki_connector.py +0 -0
  29. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/non_streaming/wiki_data_client.py +0 -0
  30. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/non_streaming/wiki_page_data.py +0 -0
  31. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/streaming/article_connector.py +0 -0
  32. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/streaming/article_data.py +0 -0
  33. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/streaming/article_data_client.py +0 -0
  34. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/snippets/streaming/run_connector.py +0 -0
  35. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/__init__.py +0 -0
  36. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/batch_processor.py +0 -0
  37. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/content_formatter.py +0 -0
  38. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/glean_client.py +0 -0
  39. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/metrics.py +0 -0
  40. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/common/mocks.py +0 -0
  41. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/__init__.py +0 -0
  42. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_connector.py +0 -0
  43. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_data_client.py +0 -0
  44. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_datasource_connector.py +0 -0
  45. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_people_connector.py +0 -0
  46. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_streaming_data_client.py +0 -0
  47. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/connectors/base_streaming_datasource_connector.py +0 -0
  48. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/models.py +0 -0
  49. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/observability/__init__.py +0 -0
  50. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/observability/observability.py +0 -0
  51. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/py.typed +0 -0
  52. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/testing/__init__.py +0 -0
  53. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/testing/connector_test_harness.py +0 -0
  54. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/testing/mock_data_source.py +0 -0
  55. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/testing/mock_glean_client.py +0 -0
  56. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/src/glean/indexing/testing/response_validator.py +0 -0
  57. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/__init__.py +0 -0
  58. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/integration_tests/__init__.py +0 -0
  59. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/__init__.py +0 -0
  60. {glean_indexing_sdk-0.0.3/tests/unit_tests/utils → glean_indexing_sdk-0.1.0/tests/unit_tests/common}/__init__.py +0 -0
  61. {glean_indexing_sdk-0.0.3/tests/unit_tests/utils → glean_indexing_sdk-0.1.0/tests/unit_tests/common}/mock_clients.py +0 -0
  62. {glean_indexing_sdk-0.0.3/tests/unit_tests/utils → glean_indexing_sdk-0.1.0/tests/unit_tests/common}/test_batch_processor.py +0 -0
  63. {glean_indexing_sdk-0.0.3/tests/unit_tests/utils → glean_indexing_sdk-0.1.0/tests/unit_tests/common}/test_content_formatter.py +0 -0
  64. {glean_indexing_sdk-0.0.3/tests/unit_tests/utils → glean_indexing_sdk-0.1.0/tests/unit_tests/common}/test_metrics.py +0 -0
  65. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_base_connector.py +0 -0
  66. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_base_data_client.py +0 -0
  67. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_base_datasource_connector.py +0 -0
  68. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/tests/unit_tests/test_base_streaming_datasource_connector.py +0 -0
  69. {glean_indexing_sdk-0.0.3 → glean_indexing_sdk-0.1.0}/uv.lock +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.commitizen]
2
2
  name = "cz_conventional_commits"
3
- version = "0.0.3"
3
+ version = "0.1.0"
4
4
  tag_format = "v$version"
5
5
  version_files = ["pyproject.toml:version", "src/glean/indexing/__init__.py:__version__"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glean-indexing-sdk
3
- Version: 0.0.3
3
+ Version: 0.1.0
4
4
  Summary: SDK for building custom Glean indexing integrations
5
5
  Project-URL: Source Code, https://github.com/glean-io/glean-indexing-sdk
6
6
  Author-email: Steve Calvert <steve.calvert@glean.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "glean-indexing-sdk"
7
- version = "0.0.3"
7
+ version = "0.1.0"
8
8
  description = "SDK for building custom Glean indexing integrations"
9
9
  authors = [{ name = "Steve Calvert", email = "steve.calvert@glean.com" }]
10
10
  readme = "README.md"
@@ -53,4 +53,4 @@ __all__ = [
53
53
  try:
54
54
  __version__ = version("glean-indexing-sdk")
55
55
  except PackageNotFoundError:
56
- __version__ = "0.0.3"
56
+ __version__ = "0.1.0"
@@ -0,0 +1,115 @@
1
+ from typing import List, Optional
2
+
3
+ from glean.api_client.models.propertydefinition import PropertyDefinition, PropertyType, UIOptions
4
+
5
+
6
+ class PropertyDefinitionBuilder:
7
+ """
8
+ Builder class for creating PropertyDefinition objects with a fluent interface.
9
+
10
+ This class provides a convenient way to build multiple PropertyDefinition objects
11
+ with proper validation and type safety.
12
+
13
+ Example:
14
+ builder = PropertyDefinitionBuilder()
15
+ properties = (builder
16
+ .add_property("title", "Title", property_type=PropertyType.TEXT)
17
+ .add_property("author", "Author", display_label_plural="Authors")
18
+ .build())
19
+ """
20
+
21
+ def __init__(self) -> None:
22
+ self.properties: List[PropertyDefinition] = []
23
+
24
+ def add_property(
25
+ self,
26
+ name: str,
27
+ display_label: str,
28
+ display_label_plural: Optional[str] = None,
29
+ property_type: PropertyType = PropertyType.TEXT,
30
+ ui_options: UIOptions = UIOptions.SEARCH_RESULT,
31
+ hide_ui_facet: bool = False,
32
+ ui_facet_order: Optional[int] = None,
33
+ group: Optional[str] = None,
34
+ ) -> "PropertyDefinitionBuilder":
35
+ """
36
+ Add a property definition to the builder.
37
+
38
+ Args:
39
+ name: The property name (must not be empty)
40
+ display_label: The display label for the property
41
+ display_label_plural: Optional plural form of the display label
42
+ property_type: The type of property (defaults to TEXT)
43
+ ui_options: UI options for the property (defaults to SEARCH_RESULT)
44
+ hide_ui_facet: Whether to hide the UI facet
45
+ ui_facet_order: Optional order for UI facet display
46
+ group: Optional group name for the property
47
+
48
+ Returns:
49
+ Self for method chaining
50
+
51
+ Raises:
52
+ ValueError: If name or display_label is empty
53
+ """
54
+ if not name or not name.strip():
55
+ raise ValueError("Property name cannot be empty")
56
+ if not display_label or not display_label.strip():
57
+ raise ValueError("Display label cannot be empty")
58
+
59
+ base_params = {
60
+ "name": name.strip(),
61
+ "display_label": display_label.strip(),
62
+ "property_type": property_type.value,
63
+ "ui_options": ui_options.value,
64
+ "hide_ui_facet": hide_ui_facet,
65
+ }
66
+
67
+ optional_params = {
68
+ k: v
69
+ for k, v in {
70
+ "display_label_plural": display_label_plural.strip()
71
+ if display_label_plural
72
+ else None,
73
+ "ui_facet_order": ui_facet_order,
74
+ "group": group.strip() if group else None,
75
+ }.items()
76
+ if v is not None
77
+ }
78
+
79
+ params = {**base_params, **optional_params}
80
+
81
+ try:
82
+ prop = PropertyDefinition(**params)
83
+ self.properties.append(prop)
84
+ except Exception as e:
85
+ raise ValueError(f"Failed to create PropertyDefinition: {e}") from e
86
+
87
+ return self
88
+
89
+ def clear(self) -> "PropertyDefinitionBuilder":
90
+ """
91
+ Clear all properties from the builder.
92
+
93
+ Returns:
94
+ Self for method chaining
95
+ """
96
+ self.properties.clear()
97
+ return self
98
+
99
+ def count(self) -> int:
100
+ """
101
+ Get the number of properties currently in the builder.
102
+
103
+ Returns:
104
+ Number of properties
105
+ """
106
+ return len(self.properties)
107
+
108
+ def build(self) -> List[PropertyDefinition]:
109
+ """
110
+ Build and return the list of PropertyDefinition objects.
111
+
112
+ Returns:
113
+ List of PropertyDefinition objects
114
+ """
115
+ return self.properties.copy()
@@ -178,11 +178,13 @@ tasks:
178
178
  {{.PYRIGHT}} {{.PYTHON_FILES}}
179
179
  fi
180
180
 
181
+ # Lint Readme task: Lint the README.md file
181
182
  lint:readme:
182
183
  desc: Lint the README.md file
183
184
  cmds:
184
185
  - npx -y markdown-code check
185
186
 
187
+ # Lint Readme fix task: Fix the README.md file
186
188
  lint:readme:fix:
187
189
  desc: Fix the README.md file
188
190
  cmds:
@@ -0,0 +1,246 @@
1
+ import pytest
2
+
3
+ from glean.api_client.models.propertydefinition import PropertyDefinition, PropertyType, UIOptions
4
+ from glean.indexing.common.property_definition_builder import PropertyDefinitionBuilder
5
+
6
+
7
+ class TestPropertyDefinitionBuilder:
8
+ def test_basic_property_creation(self):
9
+ """Test basic property creation with minimal parameters."""
10
+ builder = PropertyDefinitionBuilder()
11
+ properties = builder.add_property("test_name", "Test Label").build()
12
+
13
+ assert len(properties) == 1
14
+ prop = properties[0]
15
+ assert prop.name == "test_name"
16
+ assert prop.display_label == "Test Label"
17
+ assert prop.property_type == PropertyType.TEXT.value
18
+ assert prop.ui_options == UIOptions.SEARCH_RESULT.value
19
+ assert prop.hide_ui_facet is False
20
+
21
+ def test_property_creation_with_all_parameters(self):
22
+ """Test property creation with all parameters specified."""
23
+ builder = PropertyDefinitionBuilder()
24
+ properties = builder.add_property(
25
+ name="full_test",
26
+ display_label="Full Test Label",
27
+ display_label_plural="Full Test Labels",
28
+ property_type=PropertyType.DATE,
29
+ ui_options=UIOptions.DOC_HOVERCARD,
30
+ hide_ui_facet=True,
31
+ ui_facet_order=5,
32
+ group="test_group",
33
+ ).build()
34
+
35
+ assert len(properties) == 1
36
+ prop = properties[0]
37
+ assert prop.name == "full_test"
38
+ assert prop.display_label == "Full Test Label"
39
+ assert prop.display_label_plural == "Full Test Labels"
40
+ assert prop.property_type == PropertyType.DATE.value
41
+ assert prop.ui_options == UIOptions.DOC_HOVERCARD.value
42
+ assert prop.hide_ui_facet is True
43
+ assert prop.ui_facet_order == 5
44
+ assert prop.group == "test_group"
45
+
46
+ def test_enum_value_conversion(self):
47
+ """Test that enum objects are properly converted to string values."""
48
+ builder = PropertyDefinitionBuilder()
49
+ properties = builder.add_property(
50
+ "enum_test",
51
+ "Enum Test",
52
+ property_type=PropertyType.INT,
53
+ ui_options=UIOptions.SEARCH_RESULT,
54
+ ).build()
55
+
56
+ prop = properties[0]
57
+ assert isinstance(prop.property_type, str)
58
+ assert isinstance(prop.ui_options, str)
59
+ assert prop.property_type == PropertyType.INT.value
60
+ assert prop.ui_options == UIOptions.SEARCH_RESULT.value
61
+
62
+ def test_method_chaining(self):
63
+ """Test that the builder supports method chaining."""
64
+ builder = PropertyDefinitionBuilder()
65
+ properties = (
66
+ builder.add_property("prop1", "Property 1")
67
+ .add_property("prop2", "Property 2", property_type=PropertyType.USERID)
68
+ .add_property("prop3", "Property 3", ui_options=UIOptions.DOC_HOVERCARD)
69
+ .build()
70
+ )
71
+
72
+ assert len(properties) == 3
73
+ assert properties[0].name == "prop1"
74
+ assert properties[1].name == "prop2"
75
+ assert properties[1].property_type == PropertyType.USERID.value
76
+ assert properties[2].name == "prop3"
77
+ assert properties[2].ui_options == UIOptions.DOC_HOVERCARD.value
78
+
79
+ def test_optional_parameters_none_handling(self):
80
+ """Test that None values for optional parameters are properly handled."""
81
+ builder = PropertyDefinitionBuilder()
82
+ properties = builder.add_property(
83
+ "optional_test",
84
+ "Optional Test",
85
+ display_label_plural=None,
86
+ ui_facet_order=None,
87
+ group=None,
88
+ ).build()
89
+
90
+ prop = properties[0]
91
+ assert not hasattr(prop, "display_label_plural") or prop.display_label_plural is None
92
+ assert not hasattr(prop, "ui_facet_order") or prop.ui_facet_order is None
93
+ assert not hasattr(prop, "group") or prop.group is None
94
+
95
+ def test_string_trimming(self):
96
+ """Test that string parameters are properly trimmed."""
97
+ builder = PropertyDefinitionBuilder()
98
+ properties = builder.add_property(
99
+ " trimmed_name ",
100
+ " Trimmed Label ",
101
+ display_label_plural=" Trimmed Plural ",
102
+ group=" trimmed_group ",
103
+ ).build()
104
+
105
+ prop = properties[0]
106
+ assert prop.name == "trimmed_name"
107
+ assert prop.display_label == "Trimmed Label"
108
+ assert prop.display_label_plural == "Trimmed Plural"
109
+ assert prop.group == "trimmed_group"
110
+
111
+ def test_empty_name_validation(self):
112
+ """Test that empty or whitespace-only names raise ValueError."""
113
+ builder = PropertyDefinitionBuilder()
114
+
115
+ with pytest.raises(ValueError, match="Property name cannot be empty"):
116
+ builder.add_property("", "Valid Label")
117
+
118
+ with pytest.raises(ValueError, match="Property name cannot be empty"):
119
+ builder.add_property(" ", "Valid Label")
120
+
121
+ def test_empty_display_label_validation(self):
122
+ """Test that empty or whitespace-only display labels raise ValueError."""
123
+ builder = PropertyDefinitionBuilder()
124
+
125
+ with pytest.raises(ValueError, match="Display label cannot be empty"):
126
+ builder.add_property("valid_name", "")
127
+
128
+ with pytest.raises(ValueError, match="Display label cannot be empty"):
129
+ builder.add_property("valid_name", " ")
130
+
131
+ def test_property_definition_creation_error_handling(self):
132
+ """Test error handling when PropertyDefinition creation fails."""
133
+ builder = PropertyDefinitionBuilder()
134
+
135
+ with pytest.raises(ValueError, match="Property name cannot be empty"):
136
+ builder.add_property(None, "Test Label") # type: ignore
137
+
138
+ def test_clear_method(self):
139
+ """Test that clear method removes all properties and supports chaining."""
140
+ builder = PropertyDefinitionBuilder()
141
+ builder.add_property("prop1", "Property 1")
142
+ builder.add_property("prop2", "Property 2")
143
+
144
+ assert builder.count() == 2
145
+
146
+ result = builder.clear()
147
+ assert result is builder
148
+ assert builder.count() == 0
149
+ assert len(builder.build()) == 0
150
+
151
+ def test_count_method(self):
152
+ """Test that count method returns the correct number of properties."""
153
+ builder = PropertyDefinitionBuilder()
154
+
155
+ assert builder.count() == 0
156
+
157
+ builder.add_property("prop1", "Property 1")
158
+ assert builder.count() == 1
159
+
160
+ builder.add_property("prop2", "Property 2")
161
+ assert builder.count() == 2
162
+
163
+ builder.clear()
164
+ assert builder.count() == 0
165
+
166
+ def test_build_returns_copy(self):
167
+ """Test that build returns a copy, not the original list."""
168
+ builder = PropertyDefinitionBuilder()
169
+ builder.add_property("prop1", "Property 1")
170
+
171
+ properties1 = builder.build()
172
+ properties2 = builder.build()
173
+
174
+ assert properties1 is not properties2
175
+ assert properties1 == properties2
176
+
177
+ properties1.append("fake_property") # type: ignore
178
+ assert len(properties2) == 1
179
+
180
+ def test_empty_builder_build(self):
181
+ """Test that building an empty builder returns an empty list."""
182
+ builder = PropertyDefinitionBuilder()
183
+ properties = builder.build()
184
+
185
+ assert isinstance(properties, list)
186
+ assert len(properties) == 0
187
+
188
+ def test_all_property_types(self):
189
+ """Test that all PropertyType enum values work correctly."""
190
+ builder = PropertyDefinitionBuilder()
191
+
192
+ property_types = [
193
+ PropertyType.TEXT,
194
+ PropertyType.USERID,
195
+ PropertyType.INT,
196
+ ]
197
+
198
+ for i, prop_type in enumerate(property_types):
199
+ builder.add_property(f"prop_{i}", f"Property {i}", property_type=prop_type)
200
+
201
+ properties = builder.build()
202
+ assert len(properties) == len(property_types)
203
+
204
+ for i, prop in enumerate(properties):
205
+ assert prop.property_type == property_types[i].value
206
+
207
+ def test_all_ui_options(self):
208
+ """Test that all UIOptions enum values work correctly."""
209
+ builder = PropertyDefinitionBuilder()
210
+
211
+ ui_options = [
212
+ UIOptions.SEARCH_RESULT,
213
+ UIOptions.DOC_HOVERCARD,
214
+ ]
215
+
216
+ for i, ui_option in enumerate(ui_options):
217
+ builder.add_property(f"prop_{i}", f"Property {i}", ui_options=ui_option)
218
+
219
+ properties = builder.build()
220
+ assert len(properties) == len(ui_options)
221
+
222
+ for i, prop in enumerate(properties):
223
+ assert prop.ui_options == ui_options[i].value
224
+
225
+ def test_complex_workflow(self):
226
+ """Test a complex workflow with multiple operations."""
227
+ builder = PropertyDefinitionBuilder()
228
+
229
+ builder.add_property("title", "Title", property_type=PropertyType.TEXT)
230
+ builder.add_property("author", "Author", display_label_plural="Authors")
231
+ assert builder.count() == 2
232
+
233
+ builder.clear()
234
+ assert builder.count() == 0
235
+
236
+ properties = (
237
+ builder.add_property("name", "Full Name", group="personal")
238
+ .add_property("email", "Email Address", property_type=PropertyType.TEXT)
239
+ .add_property("department", "Department", ui_options=UIOptions.DOC_HOVERCARD)
240
+ .build()
241
+ )
242
+
243
+ assert len(properties) == 3
244
+ assert all(isinstance(prop, PropertyDefinition) for prop in properties)
245
+ assert properties[0].group == "personal"
246
+ assert properties[2].ui_options == UIOptions.DOC_HOVERCARD.value
@@ -4,7 +4,7 @@ import pytest
4
4
 
5
5
  from glean.api_client.models import EmployeeInfoDefinition
6
6
  from glean.indexing.connectors.base_people_connector import BasePeopleConnector
7
- from tests.unit_tests.utils.mock_clients import MockPeopleClient
7
+ from tests.unit_tests.common.mock_clients import MockPeopleClient
8
8
 
9
9
 
10
10
  class DummyPeopleConnector(BasePeopleConnector[dict]):
@@ -11,7 +11,7 @@ from glean.api_client.models.customdatasourceconfig import (
11
11
  from glean.api_client.models.documentdefinition import DocumentDefinition
12
12
  from glean.api_client.models.userreferencedefinition import UserReferenceDefinition
13
13
  from glean.indexing.connectors import BaseDataClient, BaseDatasourceConnector
14
- from tests.unit_tests.utils.mock_clients import MockDataSourceClient
14
+ from tests.unit_tests.common.mock_clients import MockDataSourceClient
15
15
 
16
16
 
17
17
  class CustomDatasourceConnector(BaseDatasourceConnector[dict]):