cognite-neat 1.0.32__py3-none-any.whl → 1.0.33__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.
@@ -55,7 +55,7 @@ class ContainerDiffer(ItemDiffer[ContainerRequest]):
55
55
  "constraints",
56
56
  current.constraints,
57
57
  new.constraints,
58
- add_severity=SeverityType.SAFE,
58
+ add_severity=SeverityType.WARNING,
59
59
  remove_severity=SeverityType.WARNING,
60
60
  differ=ConstraintDiffer("constraints"),
61
61
  )
@@ -12,7 +12,6 @@ from pydantic.alias_generators import to_camel
12
12
  from cognite.neat._data_model._snapshot import SchemaSnapshot
13
13
  from cognite.neat._data_model.models.dms import (
14
14
  BaseModelObject,
15
- Constraint,
16
15
  ContainerConstraintReference,
17
16
  ContainerIndexReference,
18
17
  ContainerPropertyDefinition,
@@ -20,7 +19,6 @@ from cognite.neat._data_model.models.dms import (
20
19
  ContainerRequest,
21
20
  DataModelRequest,
22
21
  DataModelResource,
23
- Index,
24
22
  T_DataModelResource,
25
23
  T_ResourceId,
26
24
  ViewRequest,
@@ -193,7 +191,9 @@ class ContainerDeploymentPlan(ResourceDeploymentPlan[ContainerReference, Contain
193
191
  def constraints_to_remove(self) -> dict[ContainerConstraintReference, RemovedField]:
194
192
  return self._get_fields_to_remove("constraints.", ContainerConstraintReference)
195
193
 
196
- def _get_fields_to_remove(self, field_prefix: str, ref_cls: type) -> dict:
194
+ def _get_fields_to_remove(
195
+ self, field_prefix: str, ref_cls: type[ContainerIndexReference] | type[ContainerConstraintReference]
196
+ ) -> dict:
197
197
  items: dict = {}
198
198
  for resource_change in self.resources:
199
199
  for change in resource_change.changes:
@@ -202,7 +202,7 @@ class ContainerDeploymentPlan(ResourceDeploymentPlan[ContainerReference, Contain
202
202
  items[
203
203
  ref_cls(
204
204
  space=resource_change.resource_id.space,
205
- external_id=resource_change.resource_id.external_id,
205
+ container_external_id=resource_change.resource_id.external_id,
206
206
  identifier=identifier,
207
207
  )
208
208
  ] = change
@@ -254,7 +254,7 @@ class ResourceDeploymentPlanList(UserList[ResourceDeploymentPlan]):
254
254
  updated_resource = resource.model_copy(update={"new_value": resource.current_value})
255
255
  elif resource.changes and resource.new_value is not None:
256
256
  # Find all field removals and update new_value accordingly.
257
- removals = [change for change in resource.changes if isinstance(change, RemovedField)]
257
+ removals: list[RemovedField] = [change for change in resource.changes if isinstance(change, RemovedField)]
258
258
  addition_paths = {change.field_path for change in resource.changes if isinstance(change, AddedField)}
259
259
  if removals:
260
260
  if resource.current_value is None:
@@ -271,6 +271,13 @@ class ResourceDeploymentPlanList(UserList[ResourceDeploymentPlan]):
271
271
  for change in resource.changes
272
272
  if not isinstance(change, RemovedField)
273
273
  or (isinstance(change, RemovedField) and change.field_path in addition_paths)
274
+ or (
275
+ isinstance(change, RemovedField)
276
+ and (
277
+ change.field_path.startswith("constraints.")
278
+ or change.field_path.startswith("indexes.")
279
+ )
280
+ )
274
281
  ],
275
282
  }
276
283
  )
@@ -320,22 +327,14 @@ class ResourceDeploymentPlanList(UserList[ResourceDeploymentPlan]):
320
327
  resource: ContainerRequest, removals: list[RemovedField], addition_paths: set[str]
321
328
  ) -> DataModelResource:
322
329
  container_properties = resource.properties.copy()
323
- indexes = (resource.indexes or {}).copy()
324
- constraints = (resource.constraints or {}).copy()
325
330
  for removal in removals:
326
331
  if removal.field_path.startswith("properties."):
327
332
  prop_key = removal.field_path.removeprefix("properties.")
328
333
  container_properties[prop_key] = cast(ContainerPropertyDefinition, removal.current_value)
329
- elif removal.field_path.startswith("indexes.") and removal.field_path not in addition_paths:
330
- # Index was removed and not re-added, so we need to restore it.
331
- index_key = removal.field_path.removeprefix("indexes.")
332
- indexes[index_key] = cast(Index, removal.current_value)
333
- elif removal.field_path.startswith("constraints.") and removal.field_path not in addition_paths:
334
- # Constraint was removed and not re-added, so we need to restore it.
335
- constraint_key = removal.field_path.removeprefix("constraints.")
336
- constraints[constraint_key] = cast(Constraint, removal.current_value)
334
+ # Note: indexes and constraints are allowed to be removed in additive mode,
335
+ # so we don't restore them here unlike properties.
337
336
  return resource.model_copy(
338
- update={"properties": container_properties, "indexes": indexes or None, "constraints": constraints or None},
337
+ update={"properties": container_properties},
339
338
  deep=True,
340
339
  )
341
340
 
@@ -185,16 +185,20 @@ class SchemaDeployer(OnSuccessResultProducer):
185
185
  continue
186
186
  current_resource = current_resources[ref]
187
187
  diffs = differ.diff(current_resource, new_resource)
188
- if (
189
- isinstance(current_resource, ContainerRequest)
190
- and isinstance(new_resource, ContainerRequest)
191
- and self.options.modus_operandi == "additive"
192
- ):
193
- # In additive mode, changes to constraints and indexes require removal and re-adding
194
- # In rebuild mode, all changes are forced via deletion and re-adding
188
+ if isinstance(current_resource, ContainerRequest) and isinstance(new_resource, ContainerRequest):
189
+ # CDF doesn't support in-place modification of constraints/indexes,
190
+ # so we transform changes to remove + add operations in both modes
195
191
  diffs = self.remove_readd_modified_indexes_and_constraints(diffs, current_resource, new_resource)
192
+
193
+ warnings = self._generate_warnings_for_constraint_and_index_changes(diffs)
196
194
  resources.append(
197
- ResourceChange(resource_id=ref, new_value=new_resource, current_value=current_resource, changes=diffs)
195
+ ResourceChange(
196
+ resource_id=ref,
197
+ new_value=new_resource,
198
+ current_value=current_resource,
199
+ changes=diffs,
200
+ message=" ".join(warnings) if warnings else None,
201
+ )
198
202
  )
199
203
 
200
204
  return plan_type(endpoint=endpoint, resources=resources)
@@ -222,19 +226,19 @@ class SchemaDeployer(OnSuccessResultProducer):
222
226
  raise RuntimeError("Bug in Neat. Malformed field path for constraint/index change.")
223
227
  # Field type is either "constraints" or "indexes"
224
228
  field_type, identifier, *_ = diff.field_path.split(".", maxsplit=2)
225
- # Mark for removal
229
+ field_path = f"{field_type}.{identifier}"
226
230
  modified_diffs.append(
227
231
  RemovedField(
228
- field_path=f"{field_type}.{identifier}",
232
+ field_path=field_path,
229
233
  item_severity=SeverityType.WARNING,
230
234
  current_value=getattr(current_resource, field_type)[identifier],
231
235
  )
232
236
  )
233
- # Mark for addition
237
+ add_severity = SeverityType.WARNING if field_type == "constraints" else SeverityType.SAFE
234
238
  modified_diffs.append(
235
239
  AddedField(
236
- field_path=f"{field_type}.{identifier}",
237
- item_severity=SeverityType.SAFE,
240
+ field_path=field_path,
241
+ item_severity=add_severity,
238
242
  new_value=getattr(new_resource, field_type)[identifier],
239
243
  )
240
244
  )
@@ -262,6 +266,29 @@ class SchemaDeployer(OnSuccessResultProducer):
262
266
  )
263
267
  return None
264
268
 
269
+ @classmethod
270
+ def _generate_warnings_for_constraint_and_index_changes(cls, diffs: list[FieldChange]) -> list[str]:
271
+ """Generate warning messages for constraint and index changes.
272
+
273
+ Args:
274
+ diffs: The list of field changes.
275
+
276
+ Returns:
277
+ A list of warning messages for field changes involving constraint and index changes.
278
+ """
279
+ warnings: list[str] = []
280
+ if any(isinstance(diff, AddedField) and diff.field_path.startswith("constraints.") for diff in diffs):
281
+ warnings.append(
282
+ "Adding constraints could cause ingestion failures if the data being ingested violates the constraint."
283
+ )
284
+ if any(
285
+ isinstance(diff, RemovedField)
286
+ and (diff.field_path.startswith("constraints.") or diff.field_path.startswith("indexes."))
287
+ for diff in diffs
288
+ ):
289
+ warnings.append("Removing constraints or indexes may affect query performance.")
290
+ return warnings
291
+
265
292
  def should_proceed_to_deploy(self, plan: Sequence[ResourceDeploymentPlan]) -> bool:
266
293
  max_severity_in_plan = SeverityType.max_severity(
267
294
  [change.severity for resource_plan in plan for change in resource_plan.resources],
@@ -6,6 +6,8 @@ from cognite.neat._utils.useful_types import ReferenceObject
6
6
  from ._container import ContainerRequest
7
7
  from ._data_model import DataModelRequest
8
8
  from ._references import (
9
+ ContainerConstraintReference,
10
+ ContainerIndexReference,
9
11
  ContainerReference,
10
12
  DataModelReference,
11
13
  SpaceReference,
@@ -18,7 +20,14 @@ DataModelResource: TypeAlias = SpaceRequest | DataModelRequest | ViewRequest | C
18
20
 
19
21
  T_DataModelResource = TypeVar("T_DataModelResource", bound=DataModelResource)
20
22
 
21
- ResourceId: TypeAlias = SpaceReference | DataModelReference | ViewReference | ContainerReference
23
+ ResourceId: TypeAlias = (
24
+ SpaceReference
25
+ | DataModelReference
26
+ | ViewReference
27
+ | ContainerReference
28
+ | ContainerIndexReference
29
+ | ContainerConstraintReference
30
+ )
22
31
 
23
32
  T_ResourceId = TypeVar("T_ResourceId", bound=ResourceId)
24
33
 
@@ -133,9 +133,43 @@ class ViewDirectReference(ReferenceObject):
133
133
  return f"{self.source!s}.{self.identifier}"
134
134
 
135
135
 
136
- class ContainerIndexReference(ContainerReference):
137
- identifier: str
136
+ class ContainerIndexReference(ReferenceObject):
137
+ """Reference to a container index for deletion API."""
138
+
139
+ space: str = Field(
140
+ description="Id of the space hosting the container.",
141
+ min_length=1,
142
+ max_length=43,
143
+ pattern=SPACE_FORMAT_PATTERN,
144
+ )
145
+ container_external_id: str = Field(
146
+ description="External-id of the container.",
147
+ min_length=1,
148
+ max_length=255,
149
+ pattern=DM_EXTERNAL_ID_PATTERN,
150
+ alias="containerExternalId",
151
+ )
152
+ identifier: str = Field(
153
+ description="Identifier of the index.",
154
+ )
138
155
 
139
156
 
140
- class ContainerConstraintReference(ContainerReference):
141
- identifier: str
157
+ class ContainerConstraintReference(ReferenceObject):
158
+ """Reference to a container constraint for deletion API."""
159
+
160
+ space: str = Field(
161
+ description="Id of the space hosting the container.",
162
+ min_length=1,
163
+ max_length=43,
164
+ pattern=SPACE_FORMAT_PATTERN,
165
+ )
166
+ container_external_id: str = Field(
167
+ description="External-id of the container.",
168
+ min_length=1,
169
+ max_length=255,
170
+ pattern=DM_EXTERNAL_ID_PATTERN,
171
+ alias="containerExternalId",
172
+ )
173
+ identifier: str = Field(
174
+ description="Identifier of the constraint.",
175
+ )
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "1.0.32"
1
+ __version__ = "1.0.33"
2
2
  __engine__ = "^2.0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-neat
3
- Version: 1.0.32
3
+ Version: 1.0.33
4
4
  Summary: Knowledge graph transformation
5
5
  Author: Nikola Vasiljevic, Anders Albert
6
6
  Author-email: Nikola Vasiljevic <nikola.vasiljevic@cognite.com>, Anders Albert <anders.albert@cognite.com>
@@ -24,12 +24,12 @@ cognite/neat/_data_model/_shared.py,sha256=H0gFqa8tKFNWuvdat5jL6OwySjCw3aQkLPY3w
24
24
  cognite/neat/_data_model/_snapshot.py,sha256=JBaKmL0Tmprz59SZ1JeB49BPMB8Hqa-OAOt0Bai8cw4,6305
25
25
  cognite/neat/_data_model/deployer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  cognite/neat/_data_model/deployer/_differ.py,sha256=1ircRBCoaFooSzMTmTZBTORHeAhDa8YtDEnVwBo6TUI,4742
27
- cognite/neat/_data_model/deployer/_differ_container.py,sha256=mcy7PhUOfnvAxnZWNoeNRmiXa8ovIn0W6YoqfzVYyiQ,14665
27
+ cognite/neat/_data_model/deployer/_differ_container.py,sha256=AGD4emPRCEdFGFN4rJzLn8JhOjbTrHtYVtXr8q9vSPY,14668
28
28
  cognite/neat/_data_model/deployer/_differ_data_model.py,sha256=iA7Xp-7NRvzZJXLLpJaLebkKKpv_VCBKPX6f-RU9wBk,1864
29
29
  cognite/neat/_data_model/deployer/_differ_space.py,sha256=J_AaqiseLpwQsOkKc7gmho4U2oSWAGVeEdQNepZiWw0,343
30
30
  cognite/neat/_data_model/deployer/_differ_view.py,sha256=g1xHwsoxFUaTOTtQa19nntKF3rxFzc2FxpKKFAUN_NE,11412
31
- cognite/neat/_data_model/deployer/data_classes.py,sha256=cq86u7wuKCcvH-A_cGI_gWzlQCTIG6mrXG2MahdiGco,27299
32
- cognite/neat/_data_model/deployer/deployer.py,sha256=lEdTh4jOwTxLkSEx-SlcnXUZyPZCUtIzop1zhAe2s44,19681
31
+ cognite/neat/_data_model/deployer/data_classes.py,sha256=HhByk8m7SbEPAKkUNDF7koRtQPHhILpVXN3f83icfKs,27028
32
+ cognite/neat/_data_model/deployer/deployer.py,sha256=CaD_N6fy2wk6TfrAyRssC65vf_FRvl3yfLQXtGUFdCo,20870
33
33
  cognite/neat/_data_model/exporters/__init__.py,sha256=AskjmB_0Vqib4kN84bWt8-M8nO42QypFf-l-E8oA5W8,482
34
34
  cognite/neat/_data_model/exporters/_api_exporter.py,sha256=nBDHx9dGbaje0T4IEQv0Kulk2Yu7FkPXgXK_MgLbE50,4948
35
35
  cognite/neat/_data_model/exporters/_base.py,sha256=rG_qAU5i5Hh5hUMep2UmDFFZID4x3LEenL6Z5C6N8GQ,646
@@ -59,10 +59,10 @@ cognite/neat/_data_model/models/dms/_constraints.py,sha256=cyGgDlByXAuSMWJg7Oc25
59
59
  cognite/neat/_data_model/models/dms/_container.py,sha256=wtQbNUwtpymltT1jav8wD4kIfjaIYnvhhz1KS0ffAbo,6044
60
60
  cognite/neat/_data_model/models/dms/_data_model.py,sha256=tq_JGNN-1JxG46bhBhunZiLedklYbDXFEfINB0x3a3Q,3219
61
61
  cognite/neat/_data_model/models/dms/_data_types.py,sha256=FMt_d5aJD-o3s9VQWyyCVlHk7D_p3RlSNXBP1OACPs4,6424
62
- cognite/neat/_data_model/models/dms/_http.py,sha256=YIRRowqkphFAYkx3foTeLyPMe9fNnmzhUCBDXe0u9Kk,926
62
+ cognite/neat/_data_model/models/dms/_http.py,sha256=FxOWb0qDKOc7urVXp8J0xl8gRV_psHWwfQLyAS8sLeM,1074
63
63
  cognite/neat/_data_model/models/dms/_indexes.py,sha256=ZtXe8ABuRcsAwRIZ9FCanS3uwZHpkOAhvDvjSXtx_Fs,900
64
64
  cognite/neat/_data_model/models/dms/_limits.py,sha256=-vwRutprJ7rPXLleSxCh_satR9AqRAvEMig5wSVBEXg,3596
65
- cognite/neat/_data_model/models/dms/_references.py,sha256=Mx_nxfvOrvAx7nvebhhbFw6eRm3nHqeFW5P5AqADUlM,3890
65
+ cognite/neat/_data_model/models/dms/_references.py,sha256=2l9ZD4ZCj-gzc0QB-rV4iVm8-fsnczziEOn2mZTaEfE,4934
66
66
  cognite/neat/_data_model/models/dms/_schema.py,sha256=2JFLcm52smzPdtZ69Lf02UbYAD8I_hpRbI7ZAzdxJJs,641
67
67
  cognite/neat/_data_model/models/dms/_space.py,sha256=mj6gID4vcAGsHNtgfXm4_4FMOQbUOkMd3HaYEdy07XM,1895
68
68
  cognite/neat/_data_model/models/dms/_types.py,sha256=5-cgC53AG186OZUqkltv7pMjcGNLuH7Etbn8IUcgk1c,447
@@ -333,9 +333,9 @@ cognite/neat/_v0/session/_template.py,sha256=BNcvrW5y7LWzRM1XFxZkfR1Nc7e8UgjBClH
333
333
  cognite/neat/_v0/session/_to.py,sha256=AnsRSDDdfFyYwSgi0Z-904X7WdLtPfLlR0x1xsu_jAo,19447
334
334
  cognite/neat/_v0/session/_wizard.py,sha256=baPJgXAAF3d1bn4nbIzon1gWfJOeS5T43UXRDJEnD3c,1490
335
335
  cognite/neat/_v0/session/exceptions.py,sha256=jv52D-SjxGfgqaHR8vnpzo0SOJETIuwbyffSWAxSDJw,3495
336
- cognite/neat/_version.py,sha256=yW-UQpMiFBxk9CkqtMOupox-RheZyNu-70iZhHFcuEI,45
336
+ cognite/neat/_version.py,sha256=1SKg9QyerZ0e2dpVZc97ky2WseNfvIsmQKsSm-Ot5NM,45
337
337
  cognite/neat/legacy.py,sha256=DMFeLCSBLT2enk-nm1KfX1rKR2DQDpxY-w6ThY0y9c8,421
338
338
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
339
- cognite_neat-1.0.32.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
340
- cognite_neat-1.0.32.dist-info/METADATA,sha256=7ZoBFgcFA7TUMrocwakJBvfuWt1Amux3_mqWNtqEMAE,6872
341
- cognite_neat-1.0.32.dist-info/RECORD,,
339
+ cognite_neat-1.0.33.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
340
+ cognite_neat-1.0.33.dist-info/METADATA,sha256=2xWjKg7ErSy3X3GJbvybZB952bXdMJPT5ETumyMEahw,6872
341
+ cognite_neat-1.0.33.dist-info/RECORD,,