Secure Send documented, and the machine form stops re-creating a controlling-PC link that already exists
This commit is contained in:
68
plugins/machines/frontend/views/controllingPc.js
Normal file
68
plugins/machines/frontend/views/controllingPc.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// What saving the machine form should do about its controlling-PC link.
|
||||
//
|
||||
// Pulled out of MachineForm.vue because the component decided this inline and
|
||||
// got it wrong in a way nothing could catch: it treated "I know the id of the
|
||||
// existing link" as "there IS an existing link". The id was read from
|
||||
// `rel.assetrelationshipid`, a key the API has never emitted - the column is
|
||||
// `relationshipid` and to_dict serialises column names - so it was undefined on
|
||||
// every load, and the two questions gave opposite answers on a machine that
|
||||
// plainly had a controlling PC.
|
||||
//
|
||||
// Both failures followed from that single conflation:
|
||||
// - Saving ANY change, a map position included, re-POSTed the link that was
|
||||
// already there. The server answered 409 "this relationship already
|
||||
// exists", correctly, and the form surfaced it as a failed save.
|
||||
// - Clearing the controlling PC did nothing at all, because "no selection and
|
||||
// no known id" reads as "nothing to do".
|
||||
//
|
||||
// So THE LOADED PC IS THE AUTHORITY on whether a link exists, and the
|
||||
// relationship id is only ever used to delete a row. If the id goes missing
|
||||
// again the worst case is a link that cannot be removed, never a duplicate
|
||||
// create.
|
||||
|
||||
/**
|
||||
* Decide what to do with the controlling-PC link on save.
|
||||
*
|
||||
* @param {object} state
|
||||
* @param {number|string|null} state.selected - PC id chosen in the form now.
|
||||
* @param {number|string|null} state.loaded - PC id the link named on load.
|
||||
* @param {number|null} state.existingRelationshipId - row id to delete, if known.
|
||||
* @returns {{action: 'none'|'unlink'|'link'|'relink', deleteId: number|null}}
|
||||
*/
|
||||
export function planControllingPcChange({ selected, loaded, existingRelationshipId } = {}) {
|
||||
const now = normalize(selected)
|
||||
const before = normalize(loaded)
|
||||
const deleteId = existingRelationshipId ?? null
|
||||
|
||||
// Unchanged is not a change. Somebody moving a marker on the map is not
|
||||
// asking to be re-linked to the same PC, and delete-then-create on an
|
||||
// untouched link opens a window where the machine has no controlling PC -
|
||||
// which resolve_asset_position walks across, so a failure between the two
|
||||
// calls would lose the machine's inherited position along with the link.
|
||||
if (now === before) {
|
||||
return { action: 'none', deleteId: null }
|
||||
}
|
||||
|
||||
if (now === null) {
|
||||
// Cleared. Nothing to delete means nothing to do, rather than an error:
|
||||
// the link is already absent, which is the state being asked for.
|
||||
return deleteId === null
|
||||
? { action: 'none', deleteId: null }
|
||||
: { action: 'unlink', deleteId }
|
||||
}
|
||||
|
||||
if (before === null) {
|
||||
return { action: 'link', deleteId: null }
|
||||
}
|
||||
|
||||
// Swapped to a different PC. The old row goes first: 'controls' is singular
|
||||
// per source, not per target, so leaving it would leave the machine with two.
|
||||
return { action: 'relink', deleteId }
|
||||
}
|
||||
|
||||
// Ids arrive as numbers from the API and strings from a <select>, and '' means
|
||||
// "nothing chosen". Comparing those raw is how an unchanged PC looks changed.
|
||||
function normalize(value) {
|
||||
if (value === null || value === undefined || value === '') return null
|
||||
return String(value)
|
||||
}
|
||||
Reference in New Issue
Block a user