cognite-neat 0.125.1__py3-none-any.whl → 0.126.1__py3-none-any.whl

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.

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

Files changed (80) hide show
  1. cognite/neat/_client/__init__.py +4 -0
  2. cognite/neat/_client/api.py +8 -0
  3. cognite/neat/_client/client.py +19 -0
  4. cognite/neat/_client/config.py +40 -0
  5. cognite/neat/_client/containers_api.py +73 -0
  6. cognite/neat/_client/data_classes.py +10 -0
  7. cognite/neat/_client/data_model_api.py +63 -0
  8. cognite/neat/_client/spaces_api.py +67 -0
  9. cognite/neat/_client/views_api.py +82 -0
  10. cognite/neat/_data_model/_analysis.py +127 -0
  11. cognite/neat/_data_model/_constants.py +59 -0
  12. cognite/neat/_data_model/_shared.py +46 -0
  13. cognite/neat/_data_model/deployer/__init__.py +0 -0
  14. cognite/neat/_data_model/deployer/_differ.py +113 -0
  15. cognite/neat/_data_model/deployer/_differ_container.py +354 -0
  16. cognite/neat/_data_model/deployer/_differ_data_model.py +29 -0
  17. cognite/neat/_data_model/deployer/_differ_space.py +9 -0
  18. cognite/neat/_data_model/deployer/_differ_view.py +194 -0
  19. cognite/neat/_data_model/deployer/data_classes.py +176 -0
  20. cognite/neat/_data_model/exporters/__init__.py +4 -0
  21. cognite/neat/_data_model/exporters/_base.py +22 -0
  22. cognite/neat/_data_model/exporters/_table_exporter/__init__.py +0 -0
  23. cognite/neat/_data_model/exporters/_table_exporter/exporter.py +106 -0
  24. cognite/neat/_data_model/exporters/_table_exporter/workbook.py +414 -0
  25. cognite/neat/_data_model/exporters/_table_exporter/writer.py +391 -0
  26. cognite/neat/_data_model/importers/__init__.py +2 -1
  27. cognite/neat/_data_model/importers/_api_importer.py +88 -0
  28. cognite/neat/_data_model/importers/_table_importer/data_classes.py +48 -8
  29. cognite/neat/_data_model/importers/_table_importer/importer.py +102 -6
  30. cognite/neat/_data_model/importers/_table_importer/reader.py +860 -0
  31. cognite/neat/_data_model/models/dms/__init__.py +19 -1
  32. cognite/neat/_data_model/models/dms/_base.py +12 -8
  33. cognite/neat/_data_model/models/dms/_constants.py +1 -1
  34. cognite/neat/_data_model/models/dms/_constraints.py +2 -1
  35. cognite/neat/_data_model/models/dms/_container.py +5 -5
  36. cognite/neat/_data_model/models/dms/_data_model.py +3 -3
  37. cognite/neat/_data_model/models/dms/_data_types.py +8 -1
  38. cognite/neat/_data_model/models/dms/_http.py +18 -0
  39. cognite/neat/_data_model/models/dms/_indexes.py +2 -1
  40. cognite/neat/_data_model/models/dms/_references.py +17 -4
  41. cognite/neat/_data_model/models/dms/_space.py +11 -7
  42. cognite/neat/_data_model/models/dms/_view_property.py +7 -4
  43. cognite/neat/_data_model/models/dms/_views.py +16 -6
  44. cognite/neat/_data_model/validation/__init__.py +0 -0
  45. cognite/neat/_data_model/validation/_base.py +16 -0
  46. cognite/neat/_data_model/validation/dms/__init__.py +9 -0
  47. cognite/neat/_data_model/validation/dms/_orchestrator.py +68 -0
  48. cognite/neat/_data_model/validation/dms/_validators.py +139 -0
  49. cognite/neat/_exceptions.py +15 -3
  50. cognite/neat/_issues.py +39 -6
  51. cognite/neat/_session/__init__.py +3 -0
  52. cognite/neat/_session/_physical.py +88 -0
  53. cognite/neat/_session/_session.py +34 -25
  54. cognite/neat/_session/_wrappers.py +61 -0
  55. cognite/neat/_state_machine/__init__.py +10 -0
  56. cognite/neat/{_session/_state_machine → _state_machine}/_base.py +11 -1
  57. cognite/neat/_state_machine/_states.py +53 -0
  58. cognite/neat/_store/__init__.py +3 -0
  59. cognite/neat/_store/_provenance.py +55 -0
  60. cognite/neat/_store/_store.py +124 -0
  61. cognite/neat/_utils/_reader.py +194 -0
  62. cognite/neat/_utils/http_client/__init__.py +14 -20
  63. cognite/neat/_utils/http_client/_client.py +22 -61
  64. cognite/neat/_utils/http_client/_data_classes.py +167 -268
  65. cognite/neat/_utils/text.py +6 -0
  66. cognite/neat/_utils/useful_types.py +26 -2
  67. cognite/neat/_version.py +1 -1
  68. cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +2 -2
  69. cognite/neat/v0/core/_data_model/importers/_spreadsheet2data_model.py +2 -2
  70. cognite/neat/v0/core/_data_model/models/entities/_single_value.py +1 -1
  71. cognite/neat/v0/core/_data_model/models/physical/_unverified.py +1 -1
  72. cognite/neat/v0/core/_data_model/models/physical/_validation.py +2 -2
  73. cognite/neat/v0/core/_data_model/models/physical/_verified.py +3 -3
  74. cognite/neat/v0/core/_data_model/transformers/_converters.py +1 -1
  75. {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/METADATA +1 -1
  76. {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/RECORD +78 -40
  77. cognite/neat/_session/_state_machine/__init__.py +0 -23
  78. cognite/neat/_session/_state_machine/_states.py +0 -150
  79. {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/WHEEL +0 -0
  80. {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,113 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Generic
3
+
4
+ from cognite.neat._utils.useful_types import T_Item
5
+
6
+ from .data_classes import (
7
+ AddedField,
8
+ ChangedField,
9
+ FieldChange,
10
+ FieldChanges,
11
+ RemovedField,
12
+ SeverityType,
13
+ )
14
+
15
+
16
+ class ItemDiffer(Generic[T_Item], ABC):
17
+ """A generic class for comparing two items of the same type and reporting the differences."""
18
+
19
+ @abstractmethod
20
+ def diff(self, current: T_Item, new: T_Item) -> list[FieldChange]:
21
+ """Compare two items and return a list of changes.
22
+
23
+ Args:
24
+ current: The resource as it is in CDF.
25
+ new: The resource as it is desired to be.
26
+
27
+ Returns:
28
+ A list of changes between the two resources.
29
+ """
30
+ raise NotImplementedError()
31
+
32
+ @classmethod
33
+ def _diff_name_description(cls, current: T_Item, new: T_Item) -> list[FieldChange]:
34
+ changes: list[FieldChange] = []
35
+ if hasattr(current, "name") and hasattr(new, "name"):
36
+ if current.name != new.name:
37
+ changes.append(
38
+ ChangedField(
39
+ item_severity=SeverityType.SAFE,
40
+ field_path="name",
41
+ current_value=current.name,
42
+ new_value=new.name,
43
+ )
44
+ )
45
+ if hasattr(current, "description") and hasattr(new, "description"):
46
+ if current.description != new.description:
47
+ changes.append(
48
+ ChangedField(
49
+ item_severity=SeverityType.SAFE,
50
+ field_path="description",
51
+ current_value=current.description,
52
+ new_value=new.description,
53
+ )
54
+ )
55
+ return changes
56
+
57
+
58
+ def field_differences(
59
+ parent_path: str,
60
+ current: dict[str, T_Item] | None,
61
+ new: dict[str, T_Item] | None,
62
+ add_severity: SeverityType,
63
+ remove_severity: SeverityType,
64
+ differ: ItemDiffer[T_Item],
65
+ ) -> list[FieldChange]:
66
+ """Diff two containers of items.
67
+
68
+ A container is for example the properties, constraints, or indexes of a container,
69
+ properties of a space, views of a data model, etc.
70
+
71
+ Args:
72
+ parent_path: The JSON path to the container being compared.
73
+ current: The items as they are in CDF.
74
+ new: The items as they are desired to be.
75
+ add_severity: The severity to assign to added items.
76
+ remove_severity: The severity to assign to removed items.
77
+ differ: The differ to use for comparing individual items.
78
+
79
+ """
80
+ changes: list[FieldChange] = []
81
+ current_map = current or {}
82
+ new_map = new or {}
83
+ current_keys = set(current_map.keys())
84
+ new_keys = set(new_map.keys())
85
+
86
+ for key in sorted(new_keys - current_keys):
87
+ item_path = f"{parent_path}.{key}"
88
+ changes.append(
89
+ AddedField(
90
+ item_severity=add_severity,
91
+ field_path=item_path,
92
+ new_value=new_map[key],
93
+ )
94
+ )
95
+
96
+ for key in sorted(current_keys - new_keys):
97
+ changes.append(
98
+ RemovedField(
99
+ item_severity=remove_severity,
100
+ field_path=f"{parent_path}.{key}",
101
+ current_value=current_map[key],
102
+ )
103
+ )
104
+
105
+ for key in sorted(current_keys & new_keys):
106
+ item_path = f"{parent_path}.{key}"
107
+ cdf_item = current_map[key]
108
+ desired_item = new_map[key]
109
+ diffs = differ.diff(cdf_item, desired_item)
110
+ if diffs:
111
+ changes.append(FieldChanges(field_path=item_path, changes=diffs))
112
+
113
+ return changes
@@ -0,0 +1,354 @@
1
+ from cognite.neat._data_model.models.dms import (
2
+ BtreeIndex,
3
+ ConstraintDefinition,
4
+ ContainerPropertyDefinition,
5
+ ContainerRequest,
6
+ EnumProperty,
7
+ EnumValue,
8
+ FloatProperty,
9
+ IndexDefinition,
10
+ ListablePropertyTypeDefinition,
11
+ PropertyTypeDefinition,
12
+ RequiresConstraintDefinition,
13
+ TextProperty,
14
+ UniquenessConstraintDefinition,
15
+ )
16
+ from cognite.neat._data_model.models.dms._data_types import Unit
17
+
18
+ from ._differ import ItemDiffer, field_differences
19
+ from .data_classes import (
20
+ AddedField,
21
+ ChangedField,
22
+ FieldChange,
23
+ FieldChanges,
24
+ RemovedField,
25
+ SeverityType,
26
+ )
27
+
28
+
29
+ class ContainerDiffer(ItemDiffer[ContainerRequest]):
30
+ def diff(self, current: ContainerRequest, new: ContainerRequest) -> list[FieldChange]:
31
+ changes = self._diff_name_description(current, new)
32
+
33
+ if current.used_for != new.used_for:
34
+ changes.append(
35
+ ChangedField(
36
+ item_severity=SeverityType.BREAKING,
37
+ field_path="usedFor",
38
+ current_value=current.used_for,
39
+ new_value=new.used_for,
40
+ )
41
+ )
42
+ changes.extend(
43
+ field_differences(
44
+ "properties",
45
+ current.properties,
46
+ new.properties,
47
+ add_severity=SeverityType.SAFE,
48
+ remove_severity=SeverityType.BREAKING,
49
+ differ=ContainerPropertyDiffer(),
50
+ )
51
+ )
52
+ changes.extend(
53
+ # MyPy fails to understand that ConstraintDefinition and Constraint are compatible here
54
+ field_differences( # type: ignore[misc]
55
+ "constraints",
56
+ current.constraints,
57
+ new.constraints,
58
+ add_severity=SeverityType.SAFE,
59
+ remove_severity=SeverityType.WARNING,
60
+ differ=ConstraintDiffer(),
61
+ )
62
+ )
63
+ changes.extend(
64
+ # MyPy fails to understand that IndexDefinition and Index are compatible here
65
+ field_differences( # type: ignore[misc]
66
+ "indexes",
67
+ current.indexes,
68
+ new.indexes,
69
+ add_severity=SeverityType.SAFE,
70
+ remove_severity=SeverityType.WARNING,
71
+ differ=IndexDiffer(),
72
+ )
73
+ )
74
+
75
+ return changes
76
+
77
+
78
+ class ContainerPropertyDiffer(ItemDiffer[ContainerPropertyDefinition]):
79
+ def diff(self, current: ContainerPropertyDefinition, new: ContainerPropertyDefinition) -> list[FieldChange]:
80
+ changes = self._diff_name_description(current, new)
81
+ diffs = DataTypeDiffer().diff(current.type, new.type)
82
+ if diffs:
83
+ changes.append(FieldChanges(field_path="type", changes=diffs))
84
+
85
+ if current.immutable != new.immutable:
86
+ changes.append(
87
+ ChangedField(
88
+ item_severity=SeverityType.BREAKING,
89
+ field_path="immutable",
90
+ current_value=current.immutable,
91
+ new_value=new.immutable,
92
+ )
93
+ )
94
+
95
+ if current.nullable != new.nullable:
96
+ changes.append(
97
+ ChangedField(
98
+ item_severity=SeverityType.BREAKING,
99
+ field_path="nullable",
100
+ current_value=current.nullable,
101
+ new_value=new.nullable,
102
+ )
103
+ )
104
+ if current.auto_increment != new.auto_increment:
105
+ changes.append(
106
+ ChangedField(
107
+ item_severity=SeverityType.BREAKING,
108
+ field_path="autoIncrement",
109
+ current_value=current.auto_increment,
110
+ new_value=new.auto_increment,
111
+ )
112
+ )
113
+
114
+ if current.default_value != new.default_value:
115
+ changes.append(
116
+ ChangedField(
117
+ item_severity=SeverityType.BREAKING,
118
+ field_path="defaultValue",
119
+ current_value=str(current.default_value),
120
+ new_value=str(new.default_value),
121
+ )
122
+ )
123
+ return changes
124
+
125
+
126
+ class ConstraintDiffer(ItemDiffer[ConstraintDefinition]):
127
+ def diff(self, current: ConstraintDefinition, new: ConstraintDefinition) -> list[FieldChange]:
128
+ changes: list[FieldChange] = []
129
+ if current.constraint_type != new.constraint_type:
130
+ changes.append(
131
+ ChangedField(
132
+ item_severity=SeverityType.WARNING,
133
+ field_path="constraintType",
134
+ current_value=current.constraint_type,
135
+ new_value=new.constraint_type,
136
+ )
137
+ )
138
+ if (
139
+ isinstance(current, RequiresConstraintDefinition)
140
+ and isinstance(new, RequiresConstraintDefinition)
141
+ and current.require != new.require
142
+ ):
143
+ changes.append(
144
+ ChangedField(
145
+ item_severity=SeverityType.WARNING,
146
+ field_path="require",
147
+ current_value=str(current.require),
148
+ new_value=str(new.require),
149
+ )
150
+ )
151
+ elif isinstance(current, UniquenessConstraintDefinition) and isinstance(new, UniquenessConstraintDefinition):
152
+ # The order of the properties matter.
153
+ if current.properties != new.properties:
154
+ changes.append(
155
+ ChangedField(
156
+ item_severity=SeverityType.WARNING,
157
+ field_path="properties",
158
+ current_value=str(current.properties),
159
+ new_value=str(new.properties),
160
+ )
161
+ )
162
+ if current.by_space != new.by_space:
163
+ changes.append(
164
+ ChangedField(
165
+ item_severity=SeverityType.WARNING,
166
+ field_path="bySpace",
167
+ current_value=current.by_space,
168
+ new_value=new.by_space,
169
+ )
170
+ )
171
+ return changes
172
+ return changes
173
+
174
+
175
+ class IndexDiffer(ItemDiffer[IndexDefinition]):
176
+ def diff(self, current: IndexDefinition, new: IndexDefinition) -> list[FieldChange]:
177
+ changes: list[FieldChange] = []
178
+ if current.index_type != new.index_type:
179
+ changes.append(
180
+ ChangedField(
181
+ item_severity=SeverityType.WARNING,
182
+ field_path="indexType",
183
+ current_value=current.index_type,
184
+ new_value=new.index_type,
185
+ )
186
+ )
187
+ else:
188
+ if current.properties != new.properties:
189
+ changes.append(
190
+ ChangedField(
191
+ item_severity=SeverityType.WARNING,
192
+ field_path="properties",
193
+ current_value=str(current.properties),
194
+ new_value=str(new.properties),
195
+ )
196
+ )
197
+ if isinstance(current, BtreeIndex) and isinstance(new, BtreeIndex):
198
+ if current.cursorable != new.cursorable:
199
+ changes.append(
200
+ ChangedField(
201
+ item_severity=SeverityType.WARNING,
202
+ field_path="cursorable",
203
+ current_value=current.cursorable,
204
+ new_value=new.cursorable,
205
+ )
206
+ )
207
+ if current.by_space != new.by_space:
208
+ changes.append(
209
+ ChangedField(
210
+ item_severity=SeverityType.WARNING,
211
+ field_path="bySpace",
212
+ current_value=current.by_space,
213
+ new_value=new.by_space,
214
+ )
215
+ )
216
+ return changes
217
+
218
+
219
+ class DataTypeDiffer(ItemDiffer[PropertyTypeDefinition]):
220
+ def diff(self, current: PropertyTypeDefinition, new: PropertyTypeDefinition) -> list[FieldChange]:
221
+ changes: list[FieldChange] = []
222
+ if current.type != new.type:
223
+ changes.append(
224
+ ChangedField(
225
+ item_severity=SeverityType.BREAKING,
226
+ field_path="type",
227
+ current_value=current.type,
228
+ new_value=new.type,
229
+ )
230
+ )
231
+ if isinstance(current, ListablePropertyTypeDefinition) and isinstance(new, ListablePropertyTypeDefinition):
232
+ changes.extend(self._check_listable_property(current, new))
233
+
234
+ if isinstance(current, TextProperty) and isinstance(new, TextProperty):
235
+ changes.extend(self._check_text_property(current, new))
236
+
237
+ if isinstance(current, FloatProperty) and isinstance(new, FloatProperty):
238
+ changes.extend(self._check_float_unit(current.unit, new.unit))
239
+
240
+ if isinstance(current, EnumProperty) and isinstance(new, EnumProperty):
241
+ changes.extend(self._check_enum_property(current, new))
242
+
243
+ return changes
244
+
245
+ def _check_listable_property(
246
+ self, current: ListablePropertyTypeDefinition, new: ListablePropertyTypeDefinition
247
+ ) -> list[FieldChange]:
248
+ changes: list[FieldChange] = []
249
+ if current.list != new.list:
250
+ changes.append(
251
+ ChangedField(
252
+ item_severity=SeverityType.BREAKING,
253
+ field_path="list",
254
+ current_value=current.list,
255
+ new_value=new.list,
256
+ )
257
+ )
258
+ if current.max_list_size != new.max_list_size:
259
+ changes.append(
260
+ ChangedField(
261
+ item_severity=SeverityType.BREAKING
262
+ if new.max_list_size is not None
263
+ and current.max_list_size is not None
264
+ and new.max_list_size < current.max_list_size
265
+ else SeverityType.WARNING,
266
+ field_path="maxListSize",
267
+ current_value=current.max_list_size,
268
+ new_value=new.max_list_size,
269
+ )
270
+ )
271
+ return changes
272
+
273
+ def _check_float_unit(self, current: Unit | None, new: Unit | None) -> list[FieldChange]:
274
+ if current is not None and new is None:
275
+ return [RemovedField(field_path="unit", item_severity=SeverityType.WARNING, current_value=current)]
276
+ elif current is None and new is not None:
277
+ return [AddedField(field_path="unit", item_severity=SeverityType.WARNING, new_value=new)]
278
+ elif current is not None and new is not None:
279
+ changes: list[FieldChange] = []
280
+ if current.external_id != new.external_id:
281
+ changes.append(
282
+ ChangedField(
283
+ item_severity=SeverityType.WARNING,
284
+ field_path="externalId",
285
+ current_value=current.external_id,
286
+ new_value=new.external_id,
287
+ )
288
+ )
289
+ if current.source_unit != new.source_unit:
290
+ changes.append(
291
+ ChangedField(
292
+ item_severity=SeverityType.WARNING,
293
+ field_path="sourceUnit",
294
+ current_value=current.source_unit,
295
+ new_value=new.source_unit,
296
+ )
297
+ )
298
+ if changes:
299
+ return [FieldChanges(field_path="unit", changes=changes)]
300
+ return [] # No changes
301
+
302
+ def _check_text_property(self, current: TextProperty, new: TextProperty) -> list[FieldChange]:
303
+ changes: list[FieldChange] = []
304
+ if current.max_text_size != new.max_text_size:
305
+ changes.append(
306
+ ChangedField(
307
+ item_severity=SeverityType.BREAKING
308
+ if new.max_text_size is not None
309
+ and current.max_text_size is not None
310
+ and new.max_text_size < current.max_text_size
311
+ else SeverityType.WARNING,
312
+ field_path="maxTextSize",
313
+ current_value=current.max_text_size,
314
+ new_value=new.max_text_size,
315
+ )
316
+ )
317
+ if current.collation != new.collation:
318
+ changes.append(
319
+ ChangedField(
320
+ item_severity=SeverityType.WARNING,
321
+ field_path="collation",
322
+ current_value=current.collation,
323
+ new_value=new.collation,
324
+ )
325
+ )
326
+ return changes
327
+
328
+ def _check_enum_property(self, current: EnumProperty, new: EnumProperty) -> list[FieldChange]:
329
+ changes: list[FieldChange] = []
330
+ if current.unknown_value != new.unknown_value:
331
+ changes.append(
332
+ ChangedField(
333
+ item_severity=SeverityType.WARNING,
334
+ field_path="unknownValue",
335
+ current_value=current.unknown_value,
336
+ new_value=new.unknown_value,
337
+ )
338
+ )
339
+ changes.extend(
340
+ field_differences(
341
+ "values",
342
+ current.values,
343
+ new.values,
344
+ add_severity=SeverityType.SAFE,
345
+ remove_severity=SeverityType.BREAKING,
346
+ differ=EnumValueDiffer(),
347
+ )
348
+ )
349
+ return changes
350
+
351
+
352
+ class EnumValueDiffer(ItemDiffer[EnumValue]):
353
+ def diff(self, current: EnumValue, new: EnumValue) -> list[FieldChange]:
354
+ return self._diff_name_description(current, new)
@@ -0,0 +1,29 @@
1
+ from cognite.neat._data_model.models.dms import (
2
+ DataModelRequest,
3
+ )
4
+
5
+ from ._differ import ItemDiffer
6
+ from .data_classes import (
7
+ ChangedField,
8
+ FieldChange,
9
+ SeverityType,
10
+ )
11
+
12
+
13
+ class DataModelDiffer(ItemDiffer[DataModelRequest]):
14
+ def diff(self, current: DataModelRequest, new: DataModelRequest) -> list[FieldChange]:
15
+ changes: list[FieldChange] = self._diff_name_description(current, new)
16
+ if current.views != new.views:
17
+ # Change of order is considered a change.
18
+ current_views = set(current.views or [])
19
+ new_views = set(new.views or [])
20
+ changes.append(
21
+ ChangedField(
22
+ field_path="views",
23
+ item_severity=SeverityType.SAFE if current_views <= new_views else SeverityType.BREAKING,
24
+ current_value=str(current.views),
25
+ new_value=str(new.views),
26
+ )
27
+ )
28
+
29
+ return changes
@@ -0,0 +1,9 @@
1
+ from cognite.neat._data_model.models.dms import SpaceRequest
2
+
3
+ from ._differ import ItemDiffer
4
+ from .data_classes import FieldChange
5
+
6
+
7
+ class SpaceDiffer(ItemDiffer[SpaceRequest]):
8
+ def diff(self, cdf_space: SpaceRequest, desired_space: SpaceRequest) -> list[FieldChange]:
9
+ return self._diff_name_description(cdf_space, desired_space)
@@ -0,0 +1,194 @@
1
+ from cognite.neat._data_model.models.dms import (
2
+ ViewPropertyDefinition,
3
+ ViewRequest,
4
+ )
5
+ from cognite.neat._data_model.models.dms._view_property import (
6
+ EdgeProperty,
7
+ ReverseDirectRelationProperty,
8
+ ViewCoreProperty,
9
+ )
10
+
11
+ from ._differ import ItemDiffer, field_differences
12
+ from .data_classes import (
13
+ ChangedField,
14
+ FieldChange,
15
+ SeverityType,
16
+ )
17
+
18
+
19
+ class ViewDiffer(ItemDiffer[ViewRequest]):
20
+ def diff(self, current: ViewRequest, new: ViewRequest) -> list[FieldChange]:
21
+ changes: list[FieldChange] = self._diff_name_description(current, new)
22
+
23
+ if current.filter != new.filter:
24
+ changes.append(
25
+ ChangedField(
26
+ field_path="filter",
27
+ item_severity=SeverityType.BREAKING,
28
+ new_value=str(new.filter),
29
+ current_value=str(current.filter),
30
+ )
31
+ )
32
+ if current.implements != new.implements:
33
+ # Note that order of implements list is significant
34
+ changes.append(
35
+ ChangedField(
36
+ field_path="implements",
37
+ item_severity=SeverityType.BREAKING,
38
+ new_value=str(new.implements),
39
+ current_value=str(current.implements),
40
+ )
41
+ )
42
+ changes.extend(
43
+ # MyPy fails to recognize that ViewPropertyDefinition and
44
+ # the union ViewRequestProperty are the same here.
45
+ field_differences( # type: ignore[misc]
46
+ "properties",
47
+ current.properties,
48
+ new.properties,
49
+ add_severity=SeverityType.SAFE,
50
+ remove_severity=SeverityType.BREAKING,
51
+ differ=ViewPropertyDiffer(),
52
+ )
53
+ )
54
+
55
+ return changes
56
+
57
+
58
+ class ViewPropertyDiffer(ItemDiffer[ViewPropertyDefinition]):
59
+ def diff(
60
+ self,
61
+ current: ViewPropertyDefinition,
62
+ new: ViewPropertyDefinition,
63
+ ) -> list[FieldChange]:
64
+ changes: list[FieldChange] = self._diff_name_description(current, new)
65
+ if current.connection_type != new.connection_type:
66
+ changes.append(
67
+ ChangedField(
68
+ field_path="connectionType",
69
+ item_severity=SeverityType.BREAKING,
70
+ new_value=new.connection_type,
71
+ current_value=current.connection_type,
72
+ )
73
+ )
74
+ elif isinstance(current, ViewCoreProperty) and isinstance(new, ViewCoreProperty):
75
+ changes.extend(self._diff_core_property(current, new))
76
+
77
+ elif isinstance(current, EdgeProperty) and isinstance(new, EdgeProperty):
78
+ changes.extend(self._diff_edge_property(current, new))
79
+
80
+ elif isinstance(current, ReverseDirectRelationProperty) and isinstance(new, ReverseDirectRelationProperty):
81
+ changes.extend(self._diff_reverse_direct_relation_property(current, new))
82
+
83
+ return changes
84
+
85
+ def _diff_core_property(
86
+ self,
87
+ current: ViewCoreProperty,
88
+ new: ViewCoreProperty,
89
+ ) -> list[FieldChange]:
90
+ changes: list[FieldChange] = []
91
+
92
+ if current.container != new.container:
93
+ changes.append(
94
+ ChangedField(
95
+ field_path="container",
96
+ # Todo check container type.
97
+ item_severity=SeverityType.BREAKING,
98
+ new_value=new.container,
99
+ current_value=current.container,
100
+ )
101
+ )
102
+ if current.container_property_identifier != new.container_property_identifier:
103
+ changes.append(
104
+ ChangedField(
105
+ field_path="containerPropertyIdentifier",
106
+ # Todo check container property type.
107
+ item_severity=SeverityType.BREAKING,
108
+ new_value=new.container_property_identifier,
109
+ current_value=current.container_property_identifier,
110
+ )
111
+ )
112
+ if current.source != new.source:
113
+ changes.append(
114
+ ChangedField(
115
+ field_path="source",
116
+ item_severity=SeverityType.BREAKING,
117
+ new_value=new.source,
118
+ current_value=current.source,
119
+ )
120
+ )
121
+
122
+ return changes
123
+
124
+ def _diff_edge_property(
125
+ self,
126
+ current: EdgeProperty,
127
+ new: EdgeProperty,
128
+ ) -> list[FieldChange]:
129
+ changes: list[FieldChange] = []
130
+ if current.source != new.source:
131
+ changes.append(
132
+ ChangedField(
133
+ field_path="source",
134
+ item_severity=SeverityType.BREAKING,
135
+ new_value=new.source,
136
+ current_value=current.source,
137
+ )
138
+ )
139
+ if current.type != new.type:
140
+ changes.append(
141
+ ChangedField(
142
+ field_path="type",
143
+ item_severity=SeverityType.BREAKING,
144
+ new_value=new.type,
145
+ current_value=current.type,
146
+ )
147
+ )
148
+ if current.edge_source != new.edge_source:
149
+ changes.append(
150
+ ChangedField(
151
+ field_path="edgeSource",
152
+ item_severity=SeverityType.BREAKING,
153
+ new_value=new.edge_source,
154
+ current_value=current.edge_source,
155
+ )
156
+ )
157
+ if current.direction != new.direction:
158
+ changes.append(
159
+ ChangedField(
160
+ field_path="direction",
161
+ item_severity=SeverityType.BREAKING,
162
+ new_value=new.direction,
163
+ current_value=current.direction,
164
+ )
165
+ )
166
+ return changes
167
+
168
+ def _diff_reverse_direct_relation_property(
169
+ self,
170
+ current: ReverseDirectRelationProperty,
171
+ new: ReverseDirectRelationProperty,
172
+ ) -> list[FieldChange]:
173
+ changes: list[FieldChange] = []
174
+ if current.source != new.source:
175
+ changes.append(
176
+ ChangedField(
177
+ field_path="source",
178
+ item_severity=SeverityType.BREAKING,
179
+ new_value=new.source,
180
+ current_value=current.source,
181
+ )
182
+ )
183
+
184
+ if current.through != new.through:
185
+ changes.append(
186
+ ChangedField(
187
+ field_path="through",
188
+ item_severity=SeverityType.BREAKING,
189
+ new_value=new.through,
190
+ current_value=current.through,
191
+ )
192
+ )
193
+
194
+ return changes