From 570a4bca694761eeb9d9fdc95b7ae752c5419ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Wed, 13 Sep 2017 08:40:25 +0200 Subject: [PATCH 1/4] Introduce a superclass for DeveloperAppAnalytics --- Apigee/ManagementAPI/Analytics.php | 254 ++++++++++++++++++ .../ManagementAPI/DeveloperAppAnalytics.php | 237 +--------------- 2 files changed, 255 insertions(+), 236 deletions(-) create mode 100644 Apigee/ManagementAPI/Analytics.php diff --git a/Apigee/ManagementAPI/Analytics.php b/Apigee/ManagementAPI/Analytics.php new file mode 100644 index 0000000..222778a --- /dev/null +++ b/Apigee/ManagementAPI/Analytics.php @@ -0,0 +1,254 @@ +init($config, ''); + $this->setEnvironment($env); + } + + /** + * Sets the environment. + * + * We avoid validating the environment here because it is rather expensive. + * If validation is needed, call isEnvironmentValid() after setting the + * environment here. + * + * @param string + * The environment, such as 'test'or 'prod'. An asterisk wildcard means + * all environments. NOTE: API endpoint's handling of asterisks is + * somewhat bug-prone; use of asterisks is highly discouraged. + */ + public function setEnvironment($env) + { + $this->environment = $env; + $envUrl = '/o/' . rawurlencode($this->config->orgName) . '/environments/' . rawurlencode($env) . '/stats/'; + $this->setBaseUrl($envUrl); + } + + /** + * Determines whether the current configured environment is valid. + * + * @return bool + */ + public function isEnvironmentValid() + { + if ($this->environment == '*') { + // Very reluctantly. Asterisk usage is bug-prone. + return true; + } + $tempUrl = '/o/' . rawurlencode($this->config->orgName) . '/environments/' . rawurlencode($this->environment); + $tempConfig = clone $this->config; + // Disable logging and all subscribers for this validation attempt. + $tempConfig->logger = new NullLogger(); + $tempConfig->subscribers = array(); + $cachedConfig = $this->config; + $this->config = $tempConfig; + // Default to invalid; a GET must succeed for it to be valid. + $valid = false; + try { + $this->get($tempUrl); + $valid = true; + } catch (ResponseException $e) { + // Do nothing; this environment is invalid. + } + $this->config = $cachedConfig; + return $valid; + } + + /** + * Returns the current environment. + * + * @return string + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * Alias of Analytics::queryEnvironments(). + * + * We used to use a caching class here, but it was non-portable. + * + * @return string[] + */ + public function getAllEnvironments() + { + return $this->queryEnvironments(); + } + + /** + * Queries Edge to get a list of all environments configured for the org. + * + * @return string[] + */ + public function queryEnvironments() + { + $env_url = '/o/' . rawurlencode($this->config->orgName) . '/environments'; + $this->setBaseUrl($env_url); + $this->get(); + $this->restoreBaseUrl(); + return $this->responseObj; + } + + /** + * Lists all valid metrics. + * + * @static + * @return string[] + */ + public static function getMetrics() + { + return array( + 'message_count' => 'Message Count', + 'message_count-first24hrs' => 'Message Count - First 24 Hours', + 'message_count-second24hrs' => 'Message Count - Second 24 Hours', + 'error_count' => 'Error Count', + 'error_count-first24hrs' => 'Error Count - First 24 Hours', + 'total_response_time' => 'Total Response Time', + 'max_response_time' => 'Maximum Response Time', + 'min_response_time' => 'Minimum Response Time' + ); + } + + /** + * Returns a keyed array of allowable time units. Array keys are machine + * names and values are human-readable names. + * + * @return string[] + */ + public static function getTimeUnits() + { + return array( + 'second' => 'Second', + 'minute' => 'Minute', + 'hour' => 'Hour', + 'day' => 'Day', + 'week' => 'Week', + 'month' => 'Month', + 'quarter' => 'Quarter', + 'year' => 'Year', + ); + } + + /** + * Validates common parameters for analytics API calls, and bundles them + * into an appropriately-structured array to be passed into an HTTP call. + * + * @static + * @param string $metric + * @param string $timeStart + * @param string $timeEnd + * @param string $timeUnit + * @param string $sortBy + * @param string $sortOrder + * Either 'ASC' or 'DESC'. + * @return string[] + * @throws ParameterException + */ + protected static function validateParameters($metric, $timeStart, $timeEnd, $timeUnit, $sortBy, $sortOrder) + { + $metricItems = preg_split('!\s*,\s*!', $metric); + if (count($metricItems) == 0) { + throw new ParameterException('Missing metric.'); + } + $validMetrics = array_keys(self::getMetrics()); + foreach ($metricItems as $metricItem) { + if (!in_array($metricItem, $validMetrics)) { + throw new ParameterException('Invalid metric.'); + } + } + $sortByItems = preg_split('!\s*,\s*!', $sortBy); + if (count($sortByItems) == 0) { + throw new ParameterException('Missing sort-by metric'); + } + foreach ($sortByItems as $sortByItem) { + if (!in_array($sortByItem, $validMetrics)) { + throw new ParameterException('Invalid sort-by metric.'); + } + } + if (!in_array($timeUnit, array_keys(self::getTimeUnits()))) { + throw new ParameterException('Invalid time unit.'); + } + $sortOrder = strtoupper($sortOrder); + if ($sortOrder != 'ASC' && $sortOrder != 'DESC') { + throw new ParameterException('Invalid sort order.'); + } + $timeRange = self::parseTime($timeStart) . '~' . self::parseTime($timeEnd); + $payload = array( + 'timeRange' => $timeRange, + 'timeUnit' => $timeUnit, + 'sortby' => join(',', $sortByItems), + 'sort' => $sortOrder, + '_optimized' => 'js', + 'select' => join(',', $metricItems), + ); + + return $payload; + } + + /** + * Parses an incoming time string or Unix timestamp into an internally-acceptable time format. + * + * If the time input cannot be parsed, an exception is thrown. + * + * @static + * @param string|int $time + * @return string + * @throws ParameterException + */ + protected static function parseTime($time) + { + $intTime = false; + if (is_int($time)) { + $intTime = $time; + } + if (preg_match('!^([0-9]{1,2})/([0-9]{1,2})/([0-9]{4}) ([0-9]{2}):([0-9]{2})$!', $time, $matches)) { + list (, $m, $d, $y, $h, $i) = $matches; + if ($m >= 0 && $m <= 12 && $d >= 1 && $d <= 31 && $h >= 0 && $h <= 12 && $i >= 0 && $i <= 59) { + $intTime = mktime($h, $i, 0, $m, $d, $y); + } + } + if ($intTime === false) { + $intTime = @strtotime($time); + } + if ($intTime === false) { + throw new ParameterException('Invalid time format.'); + } + return date('m/d/Y H:i', $intTime); + } +} diff --git a/Apigee/ManagementAPI/DeveloperAppAnalytics.php b/Apigee/ManagementAPI/DeveloperAppAnalytics.php index 2d5b065..b624e51 100644 --- a/Apigee/ManagementAPI/DeveloperAppAnalytics.php +++ b/Apigee/ManagementAPI/DeveloperAppAnalytics.php @@ -8,9 +8,6 @@ namespace Apigee\ManagementAPI; -use Apigee\Exceptions\ParameterException; -use Apigee\Exceptions\ResponseException; -use Apigee\Util\OrgConfig; use Psr\Log\NullLogger; /** @@ -18,99 +15,8 @@ * * @author djohnson */ -class DeveloperAppAnalytics extends Base +class DeveloperAppAnalytics extends Analytics { - /** - * @var string - * The environment of the app, such as 'test'or 'prod'. - */ - protected $environment; - - /** - * Initializes the environment and sets up the OrgConfig object. - * - * @param OrgConfig $config - * @param string $env - * The environment, such as 'test'or 'prod'. An asterisk wildcard means - * all environments. - */ - public function __construct(OrgConfig $config, $env = '*') - { - $this->init($config, ''); - $this->setEnvironment($env); - } - - /** - * Sets the environment. - * - * We avoid validating the environment here because it is rather expensive. - * If validation is needed, call isEnvironmentValid() after setting the - * environment here. - * - * @param string - * The environment, such as 'test'or 'prod'. An asterisk wildcard means - * all environments. NOTE: API endpoint's handling of asterisks is - * somewhat bug-prone; use of asterisks is highly discouraged. - */ - public function setEnvironment($env) - { - $this->environment = $env; - $envUrl = '/o/' . rawurlencode($this->config->orgName) . '/environments/' . rawurlencode($env) . '/stats/'; - $this->setBaseUrl($envUrl); - } - - /** - * Determines whether the current configured environment is valid. - * - * @return bool - */ - public function isEnvironmentValid() - { - if ($this->environment == '*') { - // Very reluctantly. Asterisk usage is bug-prone. - return true; - } - $tempUrl = '/o/' . rawurlencode($this->config->orgName) . '/environments/' . rawurlencode($this->environment); - $tempConfig = clone $this->config; - // Disable logging and all subscribers for this validation attempt. - $tempConfig->logger = new NullLogger(); - $tempConfig->subscribers = array(); - $cachedConfig = $this->config; - $this->config = $tempConfig; - // Default to invalid; a GET must succeed for it to be valid. - $valid = false; - try { - $this->get($tempUrl); - $valid = true; - } catch (ResponseException $e) { - // Do nothing; this environment is invalid. - } - $this->config = $cachedConfig; - return $valid; - } - - /** - * Returns the current environment. - * - * @return string - */ - public function getEnvironment() - { - return $this->environment; - } - - /** - * Alias of DeveloperAppAnalytics::queryEnvironments(). - * - * We used to use a caching class here, but it was non-portable. - * - * @return string[] - */ - public function getAllEnvironments() - { - return $this->queryEnvironments(); - } - /** * After ensuring params are valid, fetches analytics data. * @@ -199,145 +105,4 @@ public function getByAppName($devId, $appName, $metric, $tStart, $tEnd, $tUnit, } return $datapoints; } - - /** - * Queries Edge to get a list of all environments configured for the org. - * - * @return string[] - */ - public function queryEnvironments() - { - $env_url = '/o/' . rawurlencode($this->config->orgName) . '/environments'; - $this->setBaseUrl($env_url); - $this->get(); - $this->restoreBaseUrl(); - return $this->responseObj; - } - - /** - * Lists all metrics valid for Developer Apps. - * - * @static - * @return string[] - */ - public static function getMetrics() - { - return array( - 'message_count' => 'Message Count', - 'message_count-first24hrs' => 'Message Count - First 24 Hours', - 'message_count-second24hrs' => 'Message Count - Second 24 Hours', - 'error_count' => 'Error Count', - 'error_count-first24hrs' => 'Error Count - First 24 Hours', - 'total_response_time' => 'Total Response Time', - 'max_response_time' => 'Maximum Response Time', - 'min_response_time' => 'Minimum Response Time' - ); - } - - /** - * Returns a keyed array of allowable time units. Array keys are machine - * names and values are human-readable names. - * - * @return string[] - */ - public static function getTimeUnits() - { - return array( - 'second' => 'Second', - 'minute' => 'Minute', - 'hour' => 'Hour', - 'day' => 'Day', - 'week' => 'Week', - 'month' => 'Month', - 'quarter' => 'Quarter', - 'year' => 'Year', - ); - } - - /** - * Validates common parameters for analytics API calls, and bundles them - * into an appropriately-structured array to be passed into an HTTP call. - * - * @static - * @param string $metric - * @param string $timeStart - * @param string $timeEnd - * @param string $timeUnit - * @param string $sortBy - * @param string $sortOrder - * Either 'ASC' or 'DESC'. - * @return string[] - * @throws ParameterException - */ - protected static function validateParameters($metric, $timeStart, $timeEnd, $timeUnit, $sortBy, $sortOrder) - { - $metricItems = preg_split('!\s*,\s*!', $metric); - if (count($metricItems) == 0) { - throw new ParameterException('Missing metric.'); - } - $validMetrics = array_keys(self::getMetrics()); - foreach ($metricItems as $metricItem) { - if (!in_array($metricItem, $validMetrics)) { - throw new ParameterException('Invalid metric.'); - } - } - $sortByItems = preg_split('!\s*,\s*!', $sortBy); - if (count($sortByItems) == 0) { - throw new ParameterException('Missing sort-by metric'); - } - foreach ($sortByItems as $sortByItem) { - if (!in_array($sortByItem, $validMetrics)) { - throw new ParameterException('Invalid sort-by metric.'); - } - } - if (!in_array($timeUnit, array_keys(self::getTimeUnits()))) { - throw new ParameterException('Invalid time unit.'); - } - $sortOrder = strtoupper($sortOrder); - if ($sortOrder != 'ASC' && $sortOrder != 'DESC') { - throw new ParameterException('Invalid sort order.'); - } - $timeRange = self::parseTime($timeStart) . '~' . self::parseTime($timeEnd); - $payload = array( - 'timeRange' => $timeRange, - 'timeUnit' => $timeUnit, - 'sortby' => join(',', $sortByItems), - 'sort' => $sortOrder, - '_optimized' => 'js', - 'select' => join(',', $metricItems), - ); - - return $payload; - } - - /** - * Parses an incoming time string or Unix timestamp into an internally-acceptable time format. - * - * If the time input cannot be parsed, an exception is thrown. - * - * @static - * @param string|int $time - * @return string - * @throws ParameterException - */ - protected static function parseTime($time) - { - $intTime = false; - if (is_int($time)) { - $intTime = $time; - } - if (preg_match('!^([0-9]{1,2})/([0-9]{1,2})/([0-9]{4}) ([0-9]{2}):([0-9]{2})$!', $time, $matches)) { - list (, $m, $d, $y, $h, $i) = $matches; - if ($m >= 0 && $m <= 12 && $d >= 1 && $d <= 31 && $h >= 0 && $h <= 12 && $i >= 0 && $i <= 59) { - $intTime = mktime($h, $i, 0, $m, $d, $y); - } - } - if ($intTime === false) { - $intTime = @strtotime($time); - } - if ($intTime === false) { - throw new ParameterException('Invalid time format.'); - } - return date('m/d/Y H:i', $intTime); - } } From d7bd53abd8e29f3ee417108b524471ab51dca322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Mon, 18 Sep 2017 14:36:56 +0200 Subject: [PATCH 2/4] Use overridden static methods, if any --- Apigee/ManagementAPI/Analytics.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apigee/ManagementAPI/Analytics.php b/Apigee/ManagementAPI/Analytics.php index 222778a..2faacd8 100644 --- a/Apigee/ManagementAPI/Analytics.php +++ b/Apigee/ManagementAPI/Analytics.php @@ -186,7 +186,7 @@ protected static function validateParameters($metric, $timeStart, $timeEnd, $tim if (count($metricItems) == 0) { throw new ParameterException('Missing metric.'); } - $validMetrics = array_keys(self::getMetrics()); + $validMetrics = array_keys(static::getMetrics()); foreach ($metricItems as $metricItem) { if (!in_array($metricItem, $validMetrics)) { throw new ParameterException('Invalid metric.'); @@ -201,14 +201,14 @@ protected static function validateParameters($metric, $timeStart, $timeEnd, $tim throw new ParameterException('Invalid sort-by metric.'); } } - if (!in_array($timeUnit, array_keys(self::getTimeUnits()))) { + if (!in_array($timeUnit, array_keys(static::getTimeUnits()))) { throw new ParameterException('Invalid time unit.'); } $sortOrder = strtoupper($sortOrder); if ($sortOrder != 'ASC' && $sortOrder != 'DESC') { throw new ParameterException('Invalid sort order.'); } - $timeRange = self::parseTime($timeStart) . '~' . self::parseTime($timeEnd); + $timeRange = static::parseTime($timeStart) . '~' . static::parseTime($timeEnd); $payload = array( 'timeRange' => $timeRange, 'timeUnit' => $timeUnit, From ab2f585aeae78996d2f8adff860c1ffc47c35495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Mon, 18 Sep 2017 14:37:25 +0200 Subject: [PATCH 3/4] Remove useless use lines --- Apigee/ManagementAPI/DeveloperAppAnalytics.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Apigee/ManagementAPI/DeveloperAppAnalytics.php b/Apigee/ManagementAPI/DeveloperAppAnalytics.php index b624e51..1083ecb 100644 --- a/Apigee/ManagementAPI/DeveloperAppAnalytics.php +++ b/Apigee/ManagementAPI/DeveloperAppAnalytics.php @@ -8,8 +8,6 @@ namespace Apigee\ManagementAPI; -use Psr\Log\NullLogger; - /** * Exposes Developer App Analytics data from the Management API. * From 214dfd08b435c7694c3b072d7774848e1b62e29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CS=C3=89CSY=20L=C3=A1szl=C3=B3?= Date: Mon, 18 Sep 2017 14:37:54 +0200 Subject: [PATCH 4/4] Introduce ProxyAnalytics --- Apigee/ManagementAPI/ProxyAnalytics.php | 117 ++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 Apigee/ManagementAPI/ProxyAnalytics.php diff --git a/Apigee/ManagementAPI/ProxyAnalytics.php b/Apigee/ManagementAPI/ProxyAnalytics.php new file mode 100644 index 0000000..74931ce --- /dev/null +++ b/Apigee/ManagementAPI/ProxyAnalytics.php @@ -0,0 +1,117 @@ + + *
  • UNIX timestamp
  • + *
  • mm/dd/YYYY hh:ii
  • + *
  • Any other format that the underlying strtotime() PHP function + * can parse. See {@link http://php.net/strtotime}. + * It parses them out to a UNIX timestamp if possible, otherwise + * it throws an exception.
  • + * + * @param string $tEnd + * Time end, expressed as: + *
      + *
    • UNIX timestamp
    • + *
    • mm/dd/YYYY hh:ii
    • + *
    • Any other format that the underlying strtotime() PHP function + * can parse. See {@link http://php.net/strtotime}. + * It parses them out to a UNIX timestamp if possible, otherwise + * it throws an exception.
    • + *
    + * @param string $tUnit + * Time unit: a value of 'second', 'minute', 'hour', 'day', 'week', + * 'month', 'quarter', or 'year'. + * @param string $sortBy + * A comma separated list of the same values as $metric. + * @param string $sortOrder + * Either 'ASC' or 'DESC'. + * @param int $limit + * Defaults to 14400. + * @return array + */ + public function getProxyAnalytics($metric, $tStart, $tEnd, $tUnit, $sortBy, $sortOrder = 'ASC', $limit = 14400) + { + $params = static::validateParameters($metric, $tStart, $tEnd, $tUnit, $sortBy, $sortOrder); + $params['limit'] = $limit; + + $url = 'apiproxy?'; + $first = true; + foreach ($params as $name => $val) { + if ($first) { + $first = false; + } else { + $url .= '&'; + } + $url .= $name . '=' . urlencode($val); + } + $this->get($url); + $response = $this->responseObj['Response']; + + $datapoints = array(); + $timestamps = array(); + foreach ($response['TimeUnit'] as $timestamp) { + $timestamps[] = floor($timestamp / 1000); + } + if (array_key_exists('stats', $response) && array_key_exists('data', $response['stats'])) { + foreach ($response['stats']['data'] as $responseItem) { + $itemCaption = ''; + foreach ($responseItem['identifier']['names'] as $key => $value) { + if ($value == 'apiproxy') { + $itemCaption = $responseItem['identifier']['values'][$key]; + break; + } + } + foreach ($responseItem['metric'] as $array) { + $env = $array['env']; + $name = $array['name']; + $i = 0; + foreach ($array['values'] as $metricValue) { + $datapoints[$itemCaption][$name][$env][$timestamps[$i++]] = $metricValue; + } + } + } + } + return $datapoints; + } + + /** + * Lists all valid metrics. + * + * @static + * @return string[] + */ + public static function getMetrics() + { + return array( + 'sum(message_count)' => 'Message Count', + 'sum(is_error)' => 'Sum of Errors', + 'avg(target_response_time)' => 'Average Target Response Time', + 'avg(total_response_time)' => 'Average Total Response Time', + 'max(total_response_time)' => 'Maximum Total Response Time', + ); + } +}