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.
- cognite/neat/_client/__init__.py +4 -0
- cognite/neat/_client/api.py +8 -0
- cognite/neat/_client/client.py +19 -0
- cognite/neat/_client/config.py +40 -0
- cognite/neat/_client/containers_api.py +73 -0
- cognite/neat/_client/data_classes.py +10 -0
- cognite/neat/_client/data_model_api.py +63 -0
- cognite/neat/_client/spaces_api.py +67 -0
- cognite/neat/_client/views_api.py +82 -0
- cognite/neat/_data_model/_analysis.py +127 -0
- cognite/neat/_data_model/_constants.py +59 -0
- cognite/neat/_data_model/_shared.py +46 -0
- cognite/neat/_data_model/deployer/__init__.py +0 -0
- cognite/neat/_data_model/deployer/_differ.py +113 -0
- cognite/neat/_data_model/deployer/_differ_container.py +354 -0
- cognite/neat/_data_model/deployer/_differ_data_model.py +29 -0
- cognite/neat/_data_model/deployer/_differ_space.py +9 -0
- cognite/neat/_data_model/deployer/_differ_view.py +194 -0
- cognite/neat/_data_model/deployer/data_classes.py +176 -0
- cognite/neat/_data_model/exporters/__init__.py +4 -0
- cognite/neat/_data_model/exporters/_base.py +22 -0
- cognite/neat/_data_model/exporters/_table_exporter/__init__.py +0 -0
- cognite/neat/_data_model/exporters/_table_exporter/exporter.py +106 -0
- cognite/neat/_data_model/exporters/_table_exporter/workbook.py +414 -0
- cognite/neat/_data_model/exporters/_table_exporter/writer.py +391 -0
- cognite/neat/_data_model/importers/__init__.py +2 -1
- cognite/neat/_data_model/importers/_api_importer.py +88 -0
- cognite/neat/_data_model/importers/_table_importer/data_classes.py +48 -8
- cognite/neat/_data_model/importers/_table_importer/importer.py +102 -6
- cognite/neat/_data_model/importers/_table_importer/reader.py +860 -0
- cognite/neat/_data_model/models/dms/__init__.py +19 -1
- cognite/neat/_data_model/models/dms/_base.py +12 -8
- cognite/neat/_data_model/models/dms/_constants.py +1 -1
- cognite/neat/_data_model/models/dms/_constraints.py +2 -1
- cognite/neat/_data_model/models/dms/_container.py +5 -5
- cognite/neat/_data_model/models/dms/_data_model.py +3 -3
- cognite/neat/_data_model/models/dms/_data_types.py +8 -1
- cognite/neat/_data_model/models/dms/_http.py +18 -0
- cognite/neat/_data_model/models/dms/_indexes.py +2 -1
- cognite/neat/_data_model/models/dms/_references.py +17 -4
- cognite/neat/_data_model/models/dms/_space.py +11 -7
- cognite/neat/_data_model/models/dms/_view_property.py +7 -4
- cognite/neat/_data_model/models/dms/_views.py +16 -6
- cognite/neat/_data_model/validation/__init__.py +0 -0
- cognite/neat/_data_model/validation/_base.py +16 -0
- cognite/neat/_data_model/validation/dms/__init__.py +9 -0
- cognite/neat/_data_model/validation/dms/_orchestrator.py +68 -0
- cognite/neat/_data_model/validation/dms/_validators.py +139 -0
- cognite/neat/_exceptions.py +15 -3
- cognite/neat/_issues.py +39 -6
- cognite/neat/_session/__init__.py +3 -0
- cognite/neat/_session/_physical.py +88 -0
- cognite/neat/_session/_session.py +34 -25
- cognite/neat/_session/_wrappers.py +61 -0
- cognite/neat/_state_machine/__init__.py +10 -0
- cognite/neat/{_session/_state_machine → _state_machine}/_base.py +11 -1
- cognite/neat/_state_machine/_states.py +53 -0
- cognite/neat/_store/__init__.py +3 -0
- cognite/neat/_store/_provenance.py +55 -0
- cognite/neat/_store/_store.py +124 -0
- cognite/neat/_utils/_reader.py +194 -0
- cognite/neat/_utils/http_client/__init__.py +14 -20
- cognite/neat/_utils/http_client/_client.py +22 -61
- cognite/neat/_utils/http_client/_data_classes.py +167 -268
- cognite/neat/_utils/text.py +6 -0
- cognite/neat/_utils/useful_types.py +26 -2
- cognite/neat/_version.py +1 -1
- cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +2 -2
- cognite/neat/v0/core/_data_model/importers/_spreadsheet2data_model.py +2 -2
- cognite/neat/v0/core/_data_model/models/entities/_single_value.py +1 -1
- cognite/neat/v0/core/_data_model/models/physical/_unverified.py +1 -1
- cognite/neat/v0/core/_data_model/models/physical/_validation.py +2 -2
- cognite/neat/v0/core/_data_model/models/physical/_verified.py +3 -3
- cognite/neat/v0/core/_data_model/transformers/_converters.py +1 -1
- {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/METADATA +1 -1
- {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/RECORD +78 -40
- cognite/neat/_session/_state_machine/__init__.py +0 -23
- cognite/neat/_session/_state_machine/_states.py +0 -150
- {cognite_neat-0.125.1.dist-info → cognite_neat-0.126.1.dist-info}/WHEEL +0 -0
- {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
|