From: Marcin Haba Date: Tue, 6 Dec 2022 10:50:14 +0000 (+0100) Subject: baculum: Add new filters to object category sum endpoint X-Git-Tag: Release-13.0.2~40 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=735743909b5e748d5225ee4af95b8dbad1abf600;p=thirdparty%2Fbacula.git baculum: Add new filters to object category sum endpoint Changes: - add new filters - rework /objects/stats/category-sum endpoint with keeping compatibility - change /jobs/stats/sum filter into /jobs/stats/type-sum - datestart and dateend parameters are now deprecated - datestart does not have default value (previously 1 month) - update documentation --- diff --git a/gui/baculum/protected/API/Modules/ObjectManager.php b/gui/baculum/protected/API/Modules/ObjectManager.php index 39043bbcb..4d9dda06d 100644 --- a/gui/baculum/protected/API/Modules/ObjectManager.php +++ b/gui/baculum/protected/API/Modules/ObjectManager.php @@ -87,66 +87,6 @@ LEFT JOIN Job USING (JobId) ' return $obj; } - /** - * Get number object per object category. - * - * @param string $objecttype object type (usually short name such as 'm365' or 'MySQL') - * @param string $objectsource object source - * @param string $datestart start date - * @param string $dateend end date - * @return array summary in form [objectcategory => '', objecttype => '', objectsource => '', count => 0, last_job_time => ''] - */ - public function getObjectCategorySum($objecttype = null, $objectsource = null, $datestart = null, $dateend = null) { - $otype = ''; - if (!is_null($objecttype)) { - $otype = ' AND oobj.ObjectType=:objecttype '; - - } - $osource = ''; - if (!is_null($objectsource)) { - $osource = ' AND oobj.ObjectSource=:objectsource '; - } - $dformat = 'Y-m-d H:i:s'; - if (is_null($datestart)) { - $m_ago = new \DateTime('1 month ago'); - $datestart = $m_ago->format($dformat); - } - if (is_null($dateend)) { - $dateend = date($dformat); - } - - $sql = 'SELECT oobj.ObjectCategory AS objectcategory, - oobj.ObjectType AS objecttype, - oobj.ObjectSource AS objectsource, - COUNT(DISTINCT oobj.ObjectUUID) AS count, - MAX(Job.StartTime) AS last_job_time - FROM Object AS oobj - JOIN Job USING(JobId) - WHERE - Job.StartTime BETWEEN :datestart AND :dateend - AND Job.JobStatus IN (\'T\', \'W\') - AND oobj.JobId=( - SELECT MAX(iobj.JobId) FROM Object AS iobj WHERE iobj.ObjectId=oobj.ObjectId - ) - ' . $otype . $osource . ' - GROUP BY oobj.ObjectCategory, oobj.ObjectType, oobj.ObjectSource'; - - $connection = ObjectRecord::finder()->getDbConnection(); - $connection->setActive(true); - $pdo = $connection->getPdoInstance(); - $sth = $pdo->prepare($sql); - if (!is_null($objecttype)) { - $sth->bindParam(':objecttype', $objecttype, \PDO::PARAM_STR, 100); - } - if (!is_null($objectsource)) { - $sth->bindParam(':objectsource', $objectsource, \PDO::PARAM_STR, 400); - } - $sth->bindParam(':datestart', $datestart, \PDO::PARAM_STR, 19); - $sth->bindParam(':dateend', $dateend, \PDO::PARAM_STR, 19); - $sth->execute(); - return $sth->fetchAll(\PDO::FETCH_ASSOC); - } - /** * Get object size statistics. * @@ -301,4 +241,36 @@ LEFT JOIN Job USING (JobId) ' $sth->execute(); return $sth->fetchAll(\PDO::FETCH_ASSOC); } + + /** + * Get total number of objects per object category. + * + * @param array $criteria SQL query criteria + * @return array object totals + */ + public function getObjectCategorySum($criteria) { + $where = Database::getWhere($criteria); + $wh = !empty($where['where']) ? $where['where'] : ''; + + $sql = 'SELECT + oobj.ObjectCategory AS objectcategory, + oobj.ObjectType AS objecttype, + oobj.ObjectSource AS objectsource, + SUM(1) AS count, + MAX(Job.StartTime) AS last_job_time + FROM Object AS oobj + JOIN Job USING(JobId) + ' . ($wh ? $wh . ' AND ' : ' WHERE ') . ' + oobj.JobId=( + SELECT MAX(iobj.JobId) FROM Object AS iobj WHERE iobj.ObjectId=oobj.ObjectId + ) + GROUP BY objectcategory, objecttype, objectsource'; + + $connection = ObjectRecord::finder()->getDbConnection(); + $connection->setActive(true); + $pdo = $connection->getPdoInstance(); + $sth = $pdo->prepare($sql); + $sth->execute($where['params']); + return $sth->fetchAll(\PDO::FETCH_ASSOC); + } } diff --git a/gui/baculum/protected/API/Pages/API/ObjectStatsCategorySum.php b/gui/baculum/protected/API/Pages/API/ObjectStatsCategorySum.php index 66aa34fa2..f32c55242 100644 --- a/gui/baculum/protected/API/Pages/API/ObjectStatsCategorySum.php +++ b/gui/baculum/protected/API/Pages/API/ObjectStatsCategorySum.php @@ -21,6 +21,7 @@ */ use Baculum\Common\Modules\Errors\ObjectError; +use Baculum\Common\Modules\Errors\JobError; /** * Object category stats endpoint. @@ -41,6 +42,7 @@ class ObjectStatsCategorySum extends BaculumAPIServer { if ($this->Request->contains('objectsource') && $misc->isValidName($this->Request['objectsource'])) { $objectsource = $this->Request['objectsource']; } + // TODO: Remove datestart/dateend filters since they are not compatible with rest of the API $datestart = null; if ($this->Request->contains('datestart') && $misc->isValidBDateAndTime($this->Request['datestart'])) { $datestart = $this->Request['datestart']; @@ -50,13 +52,154 @@ class ObjectStatsCategorySum extends BaculumAPIServer { $dateend = $this->Request['dateend']; } - $objects = $this->getModule('object')->getObjectCategorySum( - $objecttype, - $objectsource, - $datestart, - $dateend + $jobstatus = $this->Request->contains('jobstatus') && $this->Request['jobstatus'] ? $this->Request['jobstatus'] : ''; + $starttime_from = $this->Request->contains('starttime_from') && $misc->isValidInteger($this->Request['starttime_from']) ? (int)$this->Request['starttime_from'] : null; + $starttime_to = $this->Request->contains('starttime_to') && $misc->isValidInteger($this->Request['starttime_to']) ? (int)$this->Request['starttime_to'] : null; + $jobname = $this->Request->contains('jobname') && $misc->isValidName($this->Request['jobname']) ? $this->Request['jobname'] : ''; + $clientid = $this->Request->contains('clientid') ? $this->Request['clientid'] : ''; + + if (!empty($clientid) && !$misc->isValidId($clientid)) { + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + return; + } + $client = $this->Request->contains('client') ? $this->Request['client'] : ''; + if (!empty($client) && !$misc->isValidName($client)) { + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + return; + } + + $params = []; + + if (!empty($objecttype)) { + $params['oobj.ObjectType'] = []; + $params['oobj.ObjectType'][] = [ + 'vals' => $objecttype + ]; + } + + if (!empty($objectsource)) { + $params['oobj.ObjectSource'] = []; + $params['oobj.ObjectSource'][] = [ + 'vals' => $objectsource + ]; + } + + $result = $this->getModule('bconsole')->bconsoleCommand( + $this->director, + ['.jobs'], + null, + true ); - $this->output = $objects; - $this->error = ObjectError::ERROR_NO_ERRORS; + if ($result->exitcode === 0) { + $vals = []; + if (!empty($jobname)) { + if (in_array($jobname, $result->output)) { + $vals = [$jobname]; + } + } else { + $vals = $result->output; + } + if (count($vals) == 0) { + // no $vals criteria means that user has no job resource assigned. + $this->output = []; + $this->error = JobError::ERROR_NO_ERRORS; + return; + } + + $params['Job.Name'] = []; + $params['Job.Name'][] = [ + 'operator' => 'OR', + 'vals' => $vals + ]; + } + + $error = false; + // Client name and clientid filter + if (!empty($client) || !empty($clientid)) { + $result = $this->getModule('bconsole')->bconsoleCommand( + $this->director, + ['.client'] + ); + if ($result->exitcode === 0) { + array_shift($result->output); + $cli = null; + if (!empty($client)) { + $cli = $this->getModule('client')->getClientByName($client); + } elseif (!empty($clientid)) { + $cli = $this->getModule('client')->getClientById($clientid); + } + if (is_object($cli) && in_array($cli->name, $result->output)) { + $params['Job.ClientId'] = []; + $params['Job.ClientId'][] = [ + 'operator' => 'AND', + 'vals' => [$cli->clientid] + ]; + } else { + $error = true; + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + } + } else { + $error = true; + $this->output = $result->output; + $this->error = $result->exitcode; + } + } + + $jobstatuses = array_keys($misc->getJobState()); + $sts = str_split($jobstatus); + for ($i = 0; $i < count($sts); $i++) { + if (in_array($sts[$i], $jobstatuses)) { + if (!key_exists('Job.JobStatus', $params)) { + $params['Job.JobStatus'] = [[ + 'operator' => 'OR', + 'vals' => [] + ]]; + } + $params['Job.JobStatus'][0]['vals'][] = $sts[$i]; + } + } + + // Start time range + if (!empty($starttime_from) || !empty($datestart) || !empty($starttime_to) || !empty($dateend)) { + $params['Job.StartTime'] = []; + $start = null; + if (!empty($starttime_from)) { + $start = date('Y-m-d H:i:s', $starttime_from); + } elseif (!empty($datestart)) { + $start = $datestart; + } + + if (!is_null($start)) { + $params['Job.StartTime'][] = [ + 'operator' => '>=', + 'vals' => $start + ]; + } + + $end = null; + if (!empty($starttime_to)) { + $end = date('Y-m-d H:i:s', $starttime_to); + } elseif (!empty($dateend)) { + $end = $dateend; + } + + if (!is_null($end)) { + $params['Job.StartTime'][] = [ + 'operator' => '<=', + 'vals' => $end + ]; + } + } + + if ($error === false) { + $objects = $this->getModule('object')->getObjectCategorySum( + $params + ); + $this->output = $objects; + $this->error = ObjectError::ERROR_NO_ERRORS; + } } } diff --git a/gui/baculum/protected/API/Pages/API/endpoints.xml b/gui/baculum/protected/API/Pages/API/endpoints.xml index 806d0b6df..45743da41 100644 --- a/gui/baculum/protected/API/Pages/API/endpoints.xml +++ b/gui/baculum/protected/API/Pages/API/endpoints.xml @@ -79,7 +79,7 @@ - + diff --git a/gui/baculum/protected/API/openapi_baculum.json b/gui/baculum/protected/API/openapi_baculum.json index 5dc391b7f..c3cf91599 100644 --- a/gui/baculum/protected/API/openapi_baculum.json +++ b/gui/baculum/protected/API/openapi_baculum.json @@ -2239,7 +2239,7 @@ ] } }, - "/api/v2/jobs/stats/sum": { + "/api/v2/jobs/stats/type-sum": { "get": { "tags": ["jobs"], "summary": "Total number of jobs with specific job type and job status.", @@ -6781,16 +6781,74 @@ "name": "datestart", "in": "query", "required": false, - "description": "Start date for job time range. If not given, default is used 1 month ago date.", + "description": "Start date for job time range in form YYYY-MM-DD HH:II:SS.", "schema": { "type": "string" - } + }, + "deprecated": true }, { "name": "dateend", "in": "query", "required": false, - "description": "End date for job time range. If not given, default is used current date.", + "description": "End date for job time range in form YYYY-MM-DD HH:II:SS.", + "schema": { + "type": "string" + }, + "deprecated": true + }, + { + "name": "jobstatus", + "in": "query", + "description": "Job status letter(s). Possible multiple values like 'Ef' or 'Tef'", + "required": false, + "schema": { + "type": "string", + "description": "Job status. Note, some statuses can be not visible outside (used internally by Bacula)", + "enum": ["C", "R", "B", "T", "W", "E", "e", "f", "D", "A", "I", "F", "S", "m", "M", "s", "j", "c", "d", "t", "p", "i", "a", "l", "L"] + } + }, + { + "name": "starttime_from", + "in": "query", + "required": false, + "description": "Start time from (UNIX timestamp format, seconds)", + "schema": { + "type": "integer" + } + }, + { + "name": "starttime_to", + "in": "query", + "required": false, + "description": "Start time to (UNIX timestamp format, seconds)", + "schema": { + "type": "integer" + } + }, + { + "name": "jobname", + "in": "query", + "description": "Job name", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "query", + "description": "Client identifier (can be used instead of client value)", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "client", + "in": "query", + "description": "Client name (can be used instead clientid)", + "required": false, "schema": { "type": "string" }