Skip to content

Commit

Permalink
Improve handling of Pipeline deletes (NOJIRA)
Browse files Browse the repository at this point in the history
  • Loading branch information
Benn Oshrin committed Aug 4, 2024
1 parent 3e64551 commit c997182
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 51 deletions.
43 changes: 42 additions & 1 deletion app/src/Model/Table/ExternalIdentityRolesTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Utility\Inflector;
use Cake\Validation\Validator;
use \App\Lib\Enum\ActionEnum;
use \App\Lib\Enum\ExternalIdentityStatusEnum;
Expand Down Expand Up @@ -146,19 +147,59 @@ public function initialize(array $config): void {
*/

public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $options) {
// AR-ExternalIdentityRole-1 When an External Identity Role is deleted via a Pipeline
// action, any associated Person Role will be set to the status as configured in the
// associated Pipeline, and any associated MVEAs will be deleted from the Person Role.

// Note the above does _not_ currently apply to manual deletions. It could, and
// we could walk EIR -> EI -> EISR -> EIS -> Pipeline to get the appropriate
// configuration, but for now at least we let manual operations require further
// manual work.

// Is there a Person Role associated with this EI Role?
if(!empty($entity->id)) {
$prole = $this->PersonRoles->find()
->where(['PersonRoles.source_external_identity_role_id' => $entity->id])
->contain(['AdHocAttributes', 'Addresses', 'TelephoneNumbers'])
->first();

if(!empty($prole)) {
// Unset the foreign key to the source EI Role so we don't cascade
// deletes or otherwise mess things up.

$this->llog('trace', "Removing link from PersonRole " . $prole->id . " to source ExternalIdentityRole " . $entity->id);

$prole->source_external_identity_role_id = null;

if(!empty($options['sync_status_on_delete'])) {
// Update the associated Person Role, unless it is Frozen

if(isset($prole->frozen) && $prole->frozen) {
$this->llog('trace', "Refusing to update frozen Person Role " . $prole->id . " from deleted External Identity Role " . $entity->id);
} else {
// Update the status in accordance with the Pipeline configuration
$this->llog('rule', "AR-ExternalIdentityRole-1 Updating status on PersonRole " . $prole->id . " to " . $options['sync_status_on_delete'] . " following deletion of source ExternalIdentityRole " . $entity->id);

$prole->status = $options['sync_status_on_delete'];

// Delete the MVEAs associated with this Person Role. We do this here
// rather than in syncPerson since we're doing all the other work here.
foreach([
'Addresses',
'AdHocAttributes',
'TelephoneNumbers'
] as $eirmodel) {
$aeirmodel = Inflector::underscore($eirmodel);

if(!empty($prole->$aeirmodel)) {
foreach($prole->$aeirmodel as $aeirentity) {
$this->llog('rule', "AR-ExternalIdentityRole-1 Deleted $aeirmodel " . $aeirentity->id . " for Person Role " . $prole->id);
$this->PersonRoles->$eirmodel->deleteOrFail($aeirentity);
}
}
}
}
}

$this->PersonRoles->saveOrFail($prole);
}
}
Expand Down
63 changes: 13 additions & 50 deletions app/src/Model/Table/PipelinesTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -1147,10 +1147,16 @@ protected function syncExternalIdentity(
// deletes (and to trigger ExternalIdentitiesTable callbacks) rather than
// do it model by model, below.

// This delete will cascade to ExternalIdentityRoles, where the beforeDelete
// callback will handle updating any associated Person Roles.

$this->llog('trace', "Deleting removed External Identity " . $eisRecord->external_identity_id . " for Person " . $person->id . " from EIS " . $eis->description . " (" . $eis->id . ")");

$entity = $this->Cos->People->ExternalIdentities->get($eisRecord->external_identity_id);
$this->Cos->People->ExternalIdentities->deleteOrFail($entity);
$this->Cos->People->ExternalIdentities->deleteOrFail(
$entity,
['sync_status_on_delete' => $pipeline->sync_status_on_delete]
);

return null;
} else {
Expand Down Expand Up @@ -1398,57 +1404,14 @@ protected function syncExternalIdentity(
}

if(!$found) {
if($model == 'ExternalIdentityRoles') {
// We have to handle the link to PersonRoles a bit carefully.
// First, we'll set the status of the Role in accordance with the
// Pipeline configuration. We do this here because
// ExternalIdentityRolesTable::beforeDelete() will set the Person
// Role foreign key to null to avoid problems with cascading deletes,
// but then when we sync the Person record later we won't see this
// PersonRole since the foreign key was nulled out.

// We don't set the foreign key to null here because we want it
// to be cleared regardless of how the ExternalIdentity was deleted.
// eg: If an admin deletes it, the delete should complete but there
// is no Pipeline context so the PersonRole status won't be updated.

$prole = $this->Cos->People->PersonRoles->find()
->where(['PersonRoles.source_external_identity_role_id' => $aentity->id])
->contain(['AdHocAttributes', 'Addresses', 'TelephoneNumbers'])
->first();

if(!empty($prole)) {
if(isset($prole->frozen) && $prole->frozen) {
$this->llog('trace', "Refusing to update frozen Person Role " . $prole->id . " from deleted External Identity Role " . $aentity->id);
} else {
// Update the status in accordance with the Pipeline configuration
$this->llog('trace', "Updating status on PersonRole " . $prole->id . " to " . $pipeline->sync_status_on_delete . " following deletion of source ExternalIdentityRole " . $aentity->id);

$prole->status = $pipeline->sync_status_on_delete;
$this->Cos->People->PersonRoles->saveOrFail($prole);

// Delete the MVEAs associated with this Person Role. We do this here
// rather than in syncPerson since we're doing all the other work here.
foreach([
'Addresses',
'AdHocAttributes',
'TelephoneNumbers'
] as $eirmodel) {
$aeirmodel = Inflector::underscore($eirmodel);

if(!empty($prole->$aeirmodel)) {
foreach($prole->$aeirmodel as $aeirentity) {
$this->llog('trace', "Deleted $aeirmodel " . $aeirentity->id . " for Person Role " . $prole->id);
$this->Cos->People->PersonRoles->$eirmodel->deleteOrFail($aeirentity);
}
}
}
}
}
}
// Note there is logic in ExternalIdentityRolesTable::beforeDelete()
// to handle AR-ExternalIdentityRole-1.

$this->llog('trace', "Deleted $model " . $aentity->id . " for External Identity " . $externalIdentityEntity->id);
$this->Cos->People->ExternalIdentities->$model->deleteOrFail($aentity);
$this->Cos->People->ExternalIdentities->$model->deleteOrFail(
$aentity,
['sync_status_on_delete' => $pipeline->sync_status_on_delete]
);
// Note deleted related models remain on the ExternalIdentity in case they
// are needed later in the Pipeline.
}
Expand Down

0 comments on commit c997182

Please sign in to comment.