crowdriff_api-1.x-dev/src/CrowdriffService.php
src/CrowdriffService.php
<?php
namespace Drupal\crowdriff_api;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Serialization\SerializationInterface;
use Drupal\Core\Cache\CacheFactoryInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\key\KeyRepository;
use Drupal\file\FileRepositoryInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
/**
* Crowdriff Service.
*
* @package Drupal\crowdriff_api
*/
class CrowdriffService {
use MessengerTrait;
use StringTranslationTrait;
const CROWDRIFF_FOLDERS_URL = 'folders';
const CROWDRIFF_APPS_URL = 'apps';
const CROWDRIFF_ALBUMS_URL = 'albums';
const CROWDRIFF_ASSETS_URL = 'assets';
const CROWDRIFF_CTAS_URL = 'ctas';
const CROWDRIFF_SEARCH_URL = 'search';
const CROWDRIFF_GALLERIES_URL = 'galleries';
const CROWDRIFF_GALLERY_URL = 'gallery';
const CROWDRIFF_API_TIMEOUT = 15;
const CROWDRIFF_GALLERY_V2_TYPE = 'gallery-v2';
/**
* The logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The serialization.
*
* @var \Drupal\Component\Serialization\SerializationInterface
*/
protected $json;
/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* The client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $client;
/**
* The time.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The file repository.
*
* @var \Drupal\file\FileRepositoryInterface
*/
protected $fileRepository;
/**
* The key repository object.
*
* @var \Drupal\key\KeyRepository
*/
protected $keyRepository;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandler
*/
protected $moduleHandler;
/**
* Crowdriff Service constructor.
*
* @param \Drupal\Component\Serialization\SerializationInterface $json
* The serialization.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerFactory
* The logger factory.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The config.
* @param \Drupal\Core\Cache\CacheFactoryInterface $cache
* The cache factory.
* @param \GuzzleHttp\ClientInterface $client
* The client.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\file\FileRepositoryInterface $file_repository
* The file repository.
* @param \Drupal\key\KeyRepository $keyRepository
* The key repository.
* @param \Drupal\Core\Extension\ModuleHandler $module_handler
* The module handler.
*/
public function __construct(
SerializationInterface $json,
LoggerChannelFactoryInterface $loggerFactory,
ConfigFactoryInterface $config,
CacheFactoryInterface $cache,
ClientInterface $client,
TimeInterface $time,
EntityTypeManagerInterface $entity_type_manager,
FileRepositoryInterface $file_repository,
KeyRepository $keyRepository,
ModuleHandler $module_handler
) {
$this->json = $json;
$this->logger = $loggerFactory->get('crowdriff_api');
$this->cache = $cache->get('crowdriff');
$this->config = $config->get('crowdriff_api.settings');
$this->client = $client;
$this->time = $time;
$this->entityTypeManager = $entity_type_manager;
$this->fileRepository = $file_repository;
$this->keyRepository = $keyRepository;
$this->moduleHandler = $module_handler;
}
/**
* Get configuration.
*
* @return \Drupal\Core\Config\ImmutableConfig
* Returns the configuration.
*/
public function getConfig(): ImmutableConfig {
return $this->config;
}
/**
* Get logger instance.
*
* @return \Drupal\Core\Logger\LoggerChannelInterface
* Returns the logger.
*/
public function getLogger(): LoggerChannelInterface {
return $this->logger;
}
/**
* Get the API key.
*
* @return null|string
* Returns the configured API key.
*/
public function getApiKey(): ?string {
$api_key = NULL;
$api_key_name = $this->config->get('api_key_name');
if (!empty($api_key_name)) {
$api_key = $this->keyRepository->getKey($api_key_name)->getKeyValue();
}
return $api_key;
}
/**
* Get the API url.
*
* @return null|string
* Returns the configured API url base.
*/
public function getApiUrl(): ?string {
return $this->config->get('api_url');
}
/**
* Retrieve total matched/count of assets from CrowdRiff API.
*
* @param string $path
* The path to use when calling the API.
* Refer to: https://crowdriff.readme.io/v2.0/reference.
* @param string $method
* (optional) The method to use when calling the API. Defaults to 'GET'.
* These values are supported:
* - 'GET'.
* - 'POST'.
* @param array $params
* (optional) multidimensional array of parameters used with the POST
* method
* when calling the search endpoint.
* Refer to: https://crowdriff.readme.io/v2.0/reference#search.
*
* @return int
* The total matched.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getCount(string $path, string $method = 'GET', array $params = []): int {
$totalMatched = 0;
// Build up the API call.
$api_url_base = $this->getApiUrl();
$api_key = $this->getApiKey();
if (empty($api_key)) {
$this->messenger()
->addWarning($this->t('Crowdriff API key has not been configured. Configure API key on <a href=":link">settings page</a>.', [
':link' => Url::fromRoute('crowdriff_api.config_form')
->toString(),
]));
return 0;
}
$headers = [
'Authorization' => 'Bearer ' . $api_key,
];
$options = [
'method' => $method,
'headers' => $headers,
'connect_timeout' => $this::CROWDRIFF_API_TIMEOUT,
'timeout' => $this::CROWDRIFF_API_TIMEOUT,
'read_timeout' => $this::CROWDRIFF_API_TIMEOUT,
];
if ($method == 'POST' && $params) {
$options['body'] = $this->json::encode($params);
}
$api_url = $api_url_base . '/' . $path;
try {
$response = $this->client->request($method, $api_url, $options);
}
catch (RequestException | \Exception $e) {
watchdog_exception('crowdriff_api', $e);
}
if (isset($response) && $response->getReasonPhrase() === 'OK' && !empty($body = (string) $response->getBody())) {
// Get the appropriate data from the JSON response.
$response_data = $this->json::decode($body);
$data = $response_data['data'];
$data['paging_key'] = NULL;
// Set the paging key. If on the last page and there are exactly the
// number left in the assets array, the API
// was still sending a paging key, even
// though there were no more to load.
if (isset($response_data['paging']) && $response_data['data']['matched'] !== count($response_data['data']['assets'])) {
$data['paging_key'] = $response_data['paging']['next_key'];
}
if (isset($data['matched'])) {
$totalMatched = $data['matched'];
}
}
return $totalMatched;
}
/**
* Make request to the CrowdRiff API service.
*
* @param string $path
* The path to use when calling the API.
* Refer to: https://crowdriff.readme.io/v2.0/reference.
* @param string|null $cache_key
* A unique key used for caching the data.
* @param string $method
* (optional) The method to use when calling the API. Defaults to 'GET'.
* These values are supported:
* - 'GET'.
* - 'POST'.
* @param array|null $params
* (optional) multidimensional array of parameters used with the POST
* method
* when calling the search endpoint.
* Refer to: https://crowdriff.readme.io/v2.0/reference#search.
* @param string|null $paging_key
* The Crowdriff API Supplied page key if not the first page.
*
* @return array
* The data retrieved from the API call.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function makeRequest(string $path, $cache_key = NULL, $method = 'GET', array $params = NULL, $paging_key = NULL): array {
// Check to see if API key is configured.
$api_key = $this->getApiKey();
if (empty($api_key)) {
$this->messenger()
->addWarning($this->t('Crowdriff API key has not been configured. Configure API key on <a href=":link">settings page</a>.', [
':link' => Url::fromRoute('crowdriff_api.config_form')
->toString(),
]));
return [];
}
$api_url_base = $this->getApiUrl();
// Check for cached data.
if ($this->config->get('cache') && $cache = $this->cache->get($cache_key) && !empty($cache->data)) {
// If there is cached data and it has not expired, use it.
$data = $cache->data;
}
else {
$data = [];
// Set headers.
$headers = [
'Authorization' => 'Bearer ' . $api_key,
];
// Set options.
$options = [
'method' => $method,
'headers' => $headers,
'connect_timeout' => $this::CROWDRIFF_API_TIMEOUT,
'timeout' => $this::CROWDRIFF_API_TIMEOUT,
'read_timeout' => $this::CROWDRIFF_API_TIMEOUT,
];
if ($method == 'POST' && $params) {
$options['body'] = $this->json::encode($params);
}
// Set API url.
$api_url = $api_url_base . '/' . $path;
if (!empty($paging_key)) {
$api_url = $api_url . '?after=' . $paging_key;
}
// Send request.
try {
$response = $this->client->request($method, $api_url, $options);
}
catch (RequestException | \Exception $e) {
watchdog_exception('crowdriff_api', $e);
}
// Check response.
if (isset($response) && $response->getReasonPhrase() === 'OK' && !empty($body = (string) $response->getBody())) {
// Get the appropriate data from the JSON response.
$response_data = $this->json::decode($body);
$data = $response_data['data'];
$data['paging_key'] = NULL;
// Set the paging key. If on the last page and there are exactly the
// number left in the assets array, the API
// was still sending a paging key, even
// though there were no more to load.
if (isset($response_data['paging']) && $response_data['data']['matched'] !== count($response_data['data']['assets'])) {
$data['paging_key'] = $response_data['paging']['next_key'];
}
// Set the cache.
$cache_expire = (int) $this->config->get('cache_length');
// Convert settings value from minutes to seconds and add current time.
$cache_expire = ($cache_expire * 60) + $this->time->getRequestTime();
$this->cache->set($cache_key, $data, $cache_expire);
}
else {
// If there was an error calling the API try retrieving cached data.
if (!empty($cache = $this->cache->get($cache_key, TRUE)) && !empty($cache->data)) {
$data = $cache->data;
}
// Error logging.
if (isset($response) && !empty($body = (string) $response->getBody())) {
// Check for an error message from the CrowdRiff API.
$result_data = $this->json::decode($body);
if (isset($result_data['error'])) {
$error_message = $result_data['error']['message'];
$logger_variables = [
'@error_message' => $error_message,
];
$this->logger->error('There was an error calling the CrowdRiff API. Error: @error_message.', $logger_variables);
}
else {
$this->logger->error('There was an error calling the CrowdRiff API. No usable error message was returned from the API.');
}
}
}
}
return $data;
}
/**
* Return all available folders, or a selection of folders if given IDs.
*
* @param string $folder_ids
* A comma-separated list of folder IDs to be retrieved.
*
* @return array
* An array of folders.
*
* @see https://crowdriff.readme.io/reference/retrieve-a-folder
* @see https://crowdriff.readme.io/reference/list-all-folders
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getFolders($folder_ids = NULL): array {
if (!empty($folder_ids)) {
$cache_key = 'folders:' . $folder_ids;
$url = self::CROWDRIFF_FOLDERS_URL . '/' . $folder_ids;
}
else {
$cache_key = 'folders';
$url = self::CROWDRIFF_FOLDERS_URL;
}
return $this->makeRequest($url, $cache_key);
}
/**
* Return all available apps, or a selection of apps if given IDs.
*
* @param string $app_ids
* A comma separated list of app IDs to retrieve.
*
* @return array
* An array of apps.
*
* @see https://crowdriff.readme.io/reference/retrieve-apps-by-id
* @see https://crowdriff.readme.io/reference/list-all-apps
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getApps($app_ids = NULL): array {
if (!empty($app_ids)) {
$cache_key = 'apps:' . $app_ids;
$url = self::CROWDRIFF_APPS_URL . '/' . $app_ids;
}
else {
$cache_key = 'apps';
$url = self::CROWDRIFF_APPS_URL;
}
return $this->makeRequest($url, $cache_key);
}
/**
* Get albums.
*
* @param string $album_ids
* A comma-separated list of album IDs being retrieved.
*
* @return array
* An array of albums.
*
* @see https://crowdriff.readme.io/reference/retrieve-an-album
* @see https://crowdriff.readme.io/reference/list-all-albums
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAlbums($album_ids = NULL): array {
if (!empty($album_ids)) {
$cache_key = 'albums:' . $album_ids;
$url = self::CROWDRIFF_ALBUMS_URL . '/' . $album_ids;
}
else {
$cache_key = 'albums';
$url = self::CROWDRIFF_ALBUMS_URL;
}
return $this->makeRequest($url, $cache_key);
}
/**
* Get CTAs.
*
* @param string $cta_ids
* A list of comma-separated cta_ids to be retrieved.
*
* @return array
* An array of CTAs.
*
* @see https://crowdriff.readme.io/reference/retrieve-a-cta
* @see https://crowdriff.readme.io/reference/list-all-ctas
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getCtas($cta_ids = NULL): array {
if (!empty($cta_ids)) {
$cache_key = 'ctas:' . $cta_ids;
$url = self::CROWDRIFF_CTAS_URL . '/' . $cta_ids;
}
else {
$cache_key = 'ctas';
$url = self::CROWDRIFF_CTAS_URL;
}
return $this->makeRequest($url, $cache_key);
}
/**
* Get CTA analytics.
*
* @param string $cta_id
* The CTA ID.
*
* @return array
* Returns single CTA's analytics by it's ID.
*
* @see https://crowdriff.readme.io/reference/cta-analytics
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getCtaAnalytics($cta_id): array {
$cache_key = 'cta:' . $cta_id . ':analytics';
$url = self::CROWDRIFF_CTAS_URL . '/' . $cta_id . '/analytics';
return $this->makeRequest($url, $cache_key);
}
/**
* Returns one or more assets with the provided IDs.
*
* @param string $asset_ids
* A comma-separated list of asset uuids to be retrieved.
*
* @return array
* An array of assets.
*
* @see https://crowdriff.readme.io/reference/testinput
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetsById(string $asset_ids): array {
$cache_key = 'assets:' . $asset_ids;
$url = self::CROWDRIFF_ASSETS_URL . '/' . $asset_ids;
return $this->makeRequest($url, $cache_key);
}
/**
* Get asset analytics.
*
* @param string $asset_id
* The asset UUID.
*
* @return array
* Returns insights about the asset's performance.
*
* @see https://crowdriff.readme.io/reference/asset-insights
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetAnalytics($asset_id): array {
$cache_key = 'asset:' . $asset_id . ':analytics';
$url = self::CROWDRIFF_ASSETS_URL . '/' . $asset_id . '/analytics';
return $this->makeRequest($url, $cache_key);
}
/**
* Returns the album IDs from a specific folder.
*
* @param int $folder_id
* ID of folder.
*
* @return array
* An array of album ID's.
*
* @see https://crowdriff.readme.io/reference/retrieve-a-folder
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAlbumsFromFolder($folder_id): array {
$path = self::CROWDRIFF_FOLDERS_URL . '/' . $folder_id;
$cache_key = 'folder_albums:' . $folder_id;
$data = $this->makeRequest($path, $cache_key);
return $data[$folder_id]['albums'];
}
/**
* Returns the assets from a specific album.
*
* @param array $album_ids
* ID of album(s).
* @param int $count
* The number of assets to return. Max is 100.
* @param string $paging_key
* A key returned by requests to the Crowdriff API that have more results
* than requested. Allows for paging.
*
* @return array
* An array like so:
* [
* 'assets' => [...],
* 'paging_key' => '',
* ]
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetsFromAlbums(array $album_ids, $count = 50, $paging_key = NULL): array {
$cache_key = 'assets:albums:' . implode(',', $album_ids) . ':' . $count . ':' . $paging_key;
$params = [
'search_filter' => (object) [
'albums' => $album_ids,
],
'order' => (object) [
'field' => 'created_at',
'direction' => 'dsc',
],
'count' => (int) $count,
];
$data = $this->makeRequest(self::CROWDRIFF_SEARCH_URL, $cache_key, 'POST', $params, $paging_key);
// Allow for altering assets.
if (!empty($data['assets'])) {
$this->moduleHandler->invokeAll('crowdriff_api_alter_assets', [&$data['assets']]);
}
return [
'assets' => !empty($data['assets']) ? $data['assets'] : NULL,
'paging_key' => !empty($data['paging_key']) ? $data['paging_key'] : NULL,
];
}
/**
* Returns the assets from a specific folder.
*
* @param int $folder_id
* ID of folder.
* @param int $count
* The number of assets to return. Max is 100.
* @param string $paging_key
* A key returned by requests to the Crowdriff API that have more results
* than requested. Allows for paging.
*
* @return array
* An array like so:
* [
* 'assets' => [...],
* 'paging_key' => '',
* ]
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetsFromFolder($folder_id, $count = 50, $paging_key = NULL): array {
$cache_key = 'assets:folder:' . $folder_id . ':' . $count . ':' . $paging_key;
$params = [
'search_filter' => (object) [
'albums' => $this->getAlbumsFromFolder($folder_id),
],
'order' => (object) [
'field' => 'created_at',
'direction' => 'dsc',
],
'count' => (int) $count,
];
$data = $this->makeRequest(self::CROWDRIFF_SEARCH_URL, $cache_key, 'POST', $params, $paging_key);
// Allow for altering assets.
if (!empty($data['assets'])) {
$this->moduleHandler->invokeAll('crowdriff_api_alter_assets', [&$data['assets']]);
}
return [
'assets' => !empty($data['assets']) ? $data['assets'] : NULL,
'paging_key' => !empty($data['paging_key']) ? $data['paging_key'] : NULL,
];
}
/**
* Queries the API for assets in a specific Crowdriff App.
*
* @param string $app_id
* The App ID that contains the desired assets.
* @param int $count
* The number of assets to return. Max is 100.
* @param string $paging_key
* A Crowdriff supplied key to generate the next page of an API call.
*
* @return array
* An array like so:
* [
* 'assets' => [...],
* 'paging_key' => '',
* ]
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetsFromApp($app_id, $count = 50, $paging_key = NULL): array {
$cache_key = 'assets:app:' . $app_id . ':' . $count . ':' . $paging_key;
$params = [
'search_filter' => (object) [
'apps' => [
(object) [
'id' => (string) $app_id,
],
],
],
'order' => (object) [
'field' => 'created_at',
'direction' => 'dsc',
],
'count' => (int) $count,
];
$data = $this->makeRequest(self::CROWDRIFF_SEARCH_URL, $cache_key, 'POST', $params, $paging_key);
// Allow for altering assets.
if (!empty($data['assets'])) {
$this->moduleHandler->invokeAll('crowdriff_api_alter_assets', [&$data['assets']]);
}
return [
'assets' => !empty($data['assets']) ? $data['assets'] : NULL,
'paging_key' => !empty($data['paging_key']) ? $data['paging_key'] : NULL,
];
}
/**
* Queries the API for gallery assets in a specific Crowdriff App.
*
* @param string $gallery_id
* The Gallery ID that contains the desired assets.
* @param int $count
* How many items to request from an API call.
* @param string $paging_key
* A Crowdriff supplied key to generate the next page of an API call.
* @param string $sort_field
* The sort field.
* @param string $sort_direction
* The sort direction.
*
* @return array
* An array like so:
* [
* 'assets' => [...],
* 'paging_key' => '',
* ]
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getAssetsFromGalleryApp($gallery_id, $count, $paging_key = NULL, $sort_field = 'created_at', $sort_direction = 'desc'): array {
$cache_key = 'assets:gallery:' . $gallery_id . ':' . $count . ':' . $paging_key . ':' . $sort_field . ':' . $sort_direction;
// Params/settings.
$params = [];
// Build url.
$url = self::CROWDRIFF_GALLERIES_URL . '/' . $gallery_id . '/assets';
$url .= '?sort_field=' . $sort_field;
$url .= '&sort_direction=' . $sort_direction;
$url .= '&page_size=' . $count;
if (!empty($paging_key)) {
$url .= '&page_key=' . $paging_key;
}
// Issue data call.
$data = $this->call($url, $cache_key, 'GET', $params, $paging_key);
return [
'assets' => !empty($data['assets']) ? $data['assets'] : NULL,
'paging_key' => !empty($data['paging_key']) ? $data['paging_key'] : NULL,
];
}
}
