PHRAS-4140 sb-filter-for-expiringrights (#4594)

* add 'status' filter (for "owners" job)

```json
// type
'target' => 'owners',
// filters
'databox' => 'db_databox1',
'status' => '00xxxx',
'collection' => ['test', 'boo'],
'expire_field' => 'ExpireDate',
'prior_notice' => -30,
// action
'set_status' => '01xxxx',
'alerts' => [
   [
      'method' => 'webhook',
      'recipient' => [
         'bob@a.fr',
         'joe@b.com',
      ],
   ],
],

```

* add 'set_collection' action (for "owners" job)
acts on records using api, not sql

* add negated clause on 'set_collection'
This commit is contained in:
jygaulier
2025-03-17 10:46:59 +01:00
committed by GitHub
parent ef9d9f6e5a
commit 0df5d004ae

View File

@@ -154,6 +154,9 @@ class AlertExpiringRightsCommand extends Command
return true; return true;
} }
// actions by api
$actions = [];
// build sql where clause // build sql where clause
$wheres = []; $wheres = [];
@@ -173,7 +176,7 @@ class AlertExpiringRightsCommand extends Command
$dbox = $this->databoxes[$d]['dbox']; $dbox = $this->databoxes[$d]['dbox'];
$sbas_id = $dbox->get_sbas_id(); $sbas_id = $dbox->get_sbas_id();
// filter on collections ? // filter on collections
$collList = []; $collList = [];
foreach (get_in($job, ['collection'], []) as $c) { foreach (get_in($job, ['collection'], []) as $c) {
/** @var collection $coll */ /** @var collection $coll */
@@ -190,38 +193,69 @@ class AlertExpiringRightsCommand extends Command
} }
} }
// clause on sb (negated) // filter on status
$mask = get_in($job, ['set_status']); if (($status = get_in($job, ['status'])) !== null) {
if ($mask === null) { $m = preg_replace('/[^0-1]/', 'x', trim($status));
$this->output->writeln(sprintf("<error>missing 'set_status' clause</error>")); if (strlen($m) == 0 || strlen($m) > 32) {
return false; $this->output->writeln(sprintf("<error>invalid status (%s)</error>", $status));
return false;
}
$mask_xor = str_replace(' ', '0', ltrim(str_replace(['0', 'x'], [' ', ' '], $m)));
$mask_and = str_replace(' ', '0', ltrim(str_replace(['x', '0'], [' ', '1'], $m)));
if ($mask_xor && $mask_and) {
$wheres[] = '((r.`status` ^ 0b' . $mask_xor . ') & 0b' . $mask_and . ') = 0';
}
elseif ($mask_xor) {
$wheres[] = '(r.`status` ^ 0b' . $mask_xor . ') = 0';
}
elseif ($mask_and) {
$wheres[] = '(r.`status` & 0b' . $mask_and . ') = 0';
}
} }
$m = preg_replace('/[^0-1]/', 'x', trim($mask));
if (strlen($m) > 32) { // filter on negated set_status
$this->output->writeln(sprintf("<error>status mask (%s) too long</error>", $mask)); if(($set_status = get_in($job, ['set_status'])) !== null) {
return false; $m = preg_replace('/[^0-1]/', 'x', trim($set_status));
if (strlen($m) == 0 || strlen($m) > 32) {
$this->output->writeln(sprintf("<error>invalid set_status (%s)</error>", $set_status));
return false;
}
$mask_xor = str_replace(' ', '0', ltrim(str_replace(['0', 'x'], [' ', ' '], $m)));
$mask_and = str_replace(' ', '0', ltrim(str_replace(['x', '0'], [' ', '1'], $m)));
if ($mask_xor && $mask_and) {
$wheres[] = '((r.`status` ^ 0b' . $mask_xor . ') & 0b' . $mask_and . ') != 0';
}
elseif ($mask_xor) {
$wheres[] = '(r.`status` ^ 0b' . $mask_xor . ') != 0';
}
elseif ($mask_and) {
$wheres[] = '(r.`status` & 0b' . $mask_and . ') != 0';
}
// set status by actions api
foreach (str_split(strrev($m)) as $bit => $val) {
if ($val == '0' || $val == '1') {
if(!array_key_exists('status', $actions)) {
$actions['status'] = [];
}
$actions['status'][] = ['bit' => $bit, 'state' => $val=='1'];
}
}
} }
$mask_xor = str_replace(' ', '0', ltrim(str_replace(array('0', 'x'), array(' ', ' '), $m)));
$mask_and = str_replace(' ', '0', ltrim(str_replace(array('x', '0'), array(' ', '1'), $m))); // filter on negated set_collection
if ($mask_xor && $mask_and) { if($set_collection = get_in($job, ['set_collection'])) {
$wheres[] = '((r.`status` ^ 0b' . $mask_xor . ') & 0b' . $mask_and . ') != 0'; /** @var collection $coll */
} elseif ($mask_xor) { if (($coll = get_in($this->databoxes[$sbas_id], ['collections', $set_collection])) !== null) {
$wheres[] = '(r.`status` ^ 0b' . $mask_xor . ') != 0'; $wheres[] = "r.`coll_id`!=" . $dbox->get_connection()->quote($coll->get_coll_id());
} elseif ($mask_and) {
$wheres[] = '(r.`status` & 0b' . $mask_and . ') != 0'; // change collection by actions api
} else { $actions['base_id'] = $coll->get_coll_id();
$this->output->writeln(sprintf("<error>empty status mask</error>")); }
return false; else {
} $this->output->writeln(sprintf("<error>unknown collection (%s)</error>", $c));
// set status return false;
$set_status = "`status`"; }
$set_or = str_replace(' ', '0', ltrim(str_replace(array('0', 'x'), array(' ', ' '), $m)));
$set_nand = str_replace(' ', '0', ltrim(str_replace(array('x', '1', '0'), array(' ', ' ', '1'), $m)));
if($set_or) {
$set_status = "(" . $set_status . " | 0b" . $set_or . ")";
}
if($set_nand) {
$set_status = "(" . $set_status . " & ~0b" . $set_nand . ")";
} }
// clause on expiration date // clause on expiration date
@@ -270,18 +304,30 @@ class AlertExpiringRightsCommand extends Command
$this->output->writeln(sprintf("<info>dry mode: updates on %d records NOT executed</info>", $stmt->rowCount())); $this->output->writeln(sprintf("<info>dry mode: updates on %d records NOT executed</info>", $stmt->rowCount()));
} }
if(empty($actions)) {
$this->output->writeln(sprintf("<error>no actions defined</error>"));
return false;
}
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$record = $dbox->get_record($row['record_id']); $record = $dbox->get_record($row['record_id']);
$row['collection'] = $record->getCollection()->get_name(); $row['collection'] = $record->getCollection()->get_name();
$row['title'] = $record->get_title(); $row['title'] = $record->get_title();
$records[] = $row; $records[] = $row;
$sql = "UPDATE `record` SET `status`=" . $set_status . " WHERE record_id=" . $dbox->get_connection()->quote($row['record_id']); // update by api
if ($this->input->getOption('show-sql')) { if(!empty($actions)) {
$this->output->writeln(sprintf("sql: %s", $sql)); $js = json_encode($actions);
} $this->output->writeln(sprintf("on databox(%s), record(%s) :%s js=%s \n",
if (!$this->input->getOption('dry')) { $sbas_id,
$dbox->get_connection()->exec($sql); $row['record_id'],
$this->input->getOption('dry') ? " [DRY]" : '',
$js
));
if(!$this->input->getOption('dry')) {
$record->setMetadatasByActions(json_decode($js, false)); // false: setMetadatasByActions expects object, not array !
}
} }
} }
$stmt->closeCursor(); $stmt->closeCursor();
@@ -575,12 +621,11 @@ class AlertExpiringRightsCommand extends Command
private function sanitizeJobOwners($job) private function sanitizeJobOwners($job)
{ {
return $this->sanitize( if(get_in($job, ['set_status']) === null && get_in($job, ['set_collection']) === null) {
$job, $this->output->writeln(sprintf("<error>missing 'set_status' or 'set_collection' setting</error>"));
[ return false;
'set_status' => "is_string", }
] return true;
);
} }
private function sanitizeJobDownloaders($job) private function sanitizeJobDownloaders($job)