Relationship inverse guard, audit command, widened direction fixer
This commit is contained in:
@@ -610,6 +610,12 @@ def _add_propagated(sourceid, targetid, rel, created):
|
||||
return
|
||||
if _relationship_exists(sourceid, targetid, rel.relationshiptypeid):
|
||||
return
|
||||
# Never fan out INTO an existing inverse. The same one-way rule the create
|
||||
# path enforces: if target-T->source is already stored, adding source-T->
|
||||
# target would manufacture a reciprocal pair that no user asked for, on a
|
||||
# rail that is meant to spread one direction across sibling bays.
|
||||
if _relationship_exists(targetid, sourceid, rel.relationshiptypeid):
|
||||
return
|
||||
newrel = AssetRelationship(
|
||||
sourceassetid=sourceid,
|
||||
targetassetid=targetid,
|
||||
@@ -754,6 +760,14 @@ def create_asset_relationship():
|
||||
if not db.session.get(RelationshipType, type_id):
|
||||
return error_response(ErrorCodes.NOT_FOUND, f'Relationship type {type_id} not found', http_code=404)
|
||||
|
||||
# An asset cannot relate to itself. It renders as a duplicate row on that
|
||||
# asset's own page (the card lists both ends) and means nothing.
|
||||
if source_id == target_id:
|
||||
return error_response(
|
||||
ErrorCodes.VALIDATION_ERROR,
|
||||
'An asset cannot be related to itself'
|
||||
)
|
||||
|
||||
# Check for duplicate relationship
|
||||
existing = AssetRelationship.query.filter_by(
|
||||
sourceassetid=source_id,
|
||||
@@ -768,6 +782,32 @@ def create_asset_relationship():
|
||||
http_code=409
|
||||
)
|
||||
|
||||
# And it cannot relate BOTH WAYS on a directional type. Only one direction
|
||||
# can be true - a PC drives a machine, never the reverse - but the check
|
||||
# above is keyed on (source, target, type), so the inverse used to insert
|
||||
# cleanly and both rows then rendered on both asset pages. The dialog offers
|
||||
# an `incoming` direction, so this is reachable by hand, and the legacy
|
||||
# import stores controls the wrong way round.
|
||||
#
|
||||
# Symmetric types are exempt: they store both directions ON PURPOSE and the
|
||||
# card collapses them to one entry per peer.
|
||||
reltype = db.session.get(RelationshipType, type_id)
|
||||
if reltype is not None and reltype.isdirectional:
|
||||
inverse = AssetRelationship.query.filter_by(
|
||||
sourceassetid=target_id,
|
||||
targetassetid=source_id,
|
||||
relationshiptypeid=type_id
|
||||
).first()
|
||||
if inverse is not None:
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"The reverse of this relationship already exists "
|
||||
f"(relationship {inverse.relationshipid}). A "
|
||||
f"'{reltype.relationshiptype}' link points one way only - "
|
||||
f"remove the existing one first if the direction is wrong.",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
rel = AssetRelationship(
|
||||
sourceassetid=source_id,
|
||||
targetassetid=target_id,
|
||||
|
||||
Reference in New Issue
Block a user