etsy-1.0.0-alpha3/src/EtsyService.php
src/EtsyService.php
<?php
namespace Drupal\etsy;
use \Drupal\Core\Config\ConfigFactory;
use \Drupal\Core\Config\ImmutableConfig;
use \Drupal\Core\Cache\CacheBackendInterface;
use \Drupal\oauth2_client\Service\Oauth2ClientService;
use \Drupal\etsy\EtsyServiceInterface;
use \GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
class EtsyService implements EtsyServiceInterface {
/**
* @var Drupal\Core\Config\ImmutableConfig
*/
protected $etsySettings;
/**
* @var GuzzleHttp\Client
*/
protected $httpClient;
/**
* @var Drupal\oauth2_client\Service\Oauth2ClientService
*/
protected $oauthClient;
/**
* @var Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
// Enum option values for the 'state' query parameter
const PRODUCT_STATE_ACTIVE = 'active';
const PRODUCT_STATE_INACTIVE = 'inactive';
const PRODUCT_STATE_SOLD_OUT = 'sold_out';
const PRODUCT_STATE_DRAFT = 'draft';
const PRODUCT_STATE_EXPIRED = 'expired';
// Enum option values for the 'includes' query parameter
const INCLUDES_SHIPPING = 'Shipping';
const INCLUDES_IMAGES = 'Images';
const INCLUDES_SHOP = 'Shop';
const INCLUDES_USER = 'User';
const INCLUDE_TRANSLATIONS = 'Translations';
const INCLUDE_INVENTORY = 'Inventory';
const INCLUDE_VIDEOS = 'Videos';
const ENDPOINT = 'https://openapi.etsy.com/v3/application/';
/**
* Constructor
*
* @param \GuzzleHttp\Client $httpClient
* @param \Drupal\Core\Config\ConfigFactory $configFactory
* @param \Drupal\oauth2_client\Service\Oauth2ClientService $oauthClient
*/
public function __construct(Client $httpClient, ConfigFactory $configFactory, Oauth2ClientService $oauthClient, LoggerInterface $logger, CacheBackendInterface $cache) {
$this->httpClient = $httpClient;
$this->oauthClient = $oauthClient;
$this->etsySettings = $configFactory->get('etsy.settings');
$this->logger = $logger;
$this->cache = $cache;
}
/**
* {@inheritDoc}
*/
public function ping(): mixed {
return $this->call('openapi-ping');
}
/**
* {@inheritDoc}
*/
public function getMe(): mixed {
$shopId = $this->etsySettings->get('shop_id');
return $this->call("users/me");
}
/**
* {@inheritDoc}
*/
public function shopInfo($key = NULL): mixed {
$shopId = $this->etsySettings->get('shop_id');
$data = FALSE;
if ($this->etsySettings->get('cache_lifetime') > 0) {
$cid = 'etsy:shop_info:' . $this->etsySettings->get('shop_id');
if ($cache = $this->cache->get($cid)) {
$data = $cache->data;
}
else {
$response = $this->call("shops/{$shopId}");
if (!isset($response->error)) {
$data = $response;
$this->cache->set($cid, $data, (time() + $this->etsySettings->get('cache_lifetime')));
}
else {
return $response;
}
}
}
else {
$data = $this->call("shops/{$shopId}");
}
if (!isset($data->error) && !is_null($key)) {
$objVars = get_object_vars($data);
return $objVars[$key] ?? NULL;
}
return $data;
}
/**
* {@inheritDoc}
*/
public function getListingsByShop($limit = 25, $offset = 0, $includes = [], $state = NULL): mixed {
if (!is_array($includes)) {
throw new \Exception('Invalid type for $includes. This parameter must be an array.');
}
$params = [
'limit' => $limit,
'offset' => $offset,
];
if (!empty($includes)) {
$params['includes'] = implode(',', $includes);
}
if (!is_null($state)
&& !in_array($state, [
self::PRODUCT_STATE_ACTIVE,
self::PRODUCT_STATE_INACTIVE,
self::PRODUCT_STATE_SOLD_OUT,
self::PRODUCT_STATE_DRAFT,
self::PRODUCT_STATE_EXPIRED,
])) {
throw new \Exception("Invalid state.", 1001);
}
elseif (!is_null($state)) {
$params['state'] = $state;
}
$shopId = $this->etsySettings->get('shop_id');
if ($this->etsySettings->get('cache_lifetime') > 0) {
$cid = 'etsy:shop_listings:' . $this->etsySettings->get('shop_id');
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
else {
$response = $this->call("shops/{$shopId}/listings", 'GET', $params);
if (isset($response->error)) {
$data = $response;
$this->cache->set($cid, $data, (time() + $this->etsySettings->get('cache_lifetime')));
return $data;
}
else {
return $response;
}
}
}
else {
return $this->call("shops/{$shopId}/listings", 'GET', $params);
}
}
/**
* {@inheritDoc}
*/
public function getListingById(int $listingId, $includes = []): mixed {
if (!is_array($includes)) {
throw new \Exception('Invalid type for $includes. This parameter must be an array.');
}
$params = [];
if (!empty($includes)) {
$params['includes'] = implode(',', $includes);
}
if ($this->etsySettings->get('cache_lifetime') > 0) {
$cid = 'etsy:listing:' . $listingId;
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
else {
$response = $this->call("listings/{$listingId}", 'GET', $params);
if (!isset($response->error)) {
$data = $response;
$this->cache->set($cid, $data, (time() + $this->etsySettings->get('cache_lifetime')));
return $data;
}
else {
return $response;
}
}
}
else {
return $this->call("listings/{$listingId}", 'GET', $params);
}
}
/**
* {@inheritDoc}
*/
public function getShopSections(): object|bool {
$shopId = $this->etsySettings->get('shop_id');
if ($this->etsySettings->get('cache_lifetime') > 0) {
$cid = 'etsy:shop_sections:' . $this->etsySettings->get('shop_id');
if ($cache = $this->cache->get($cid)) {
return $cache->data;
}
else {
$response = $this->call("shops/{$shopId}/sections");
if (!isset($response->error)) {
$data = $response;
$this->cache->set($cid, $data, (time() + $this->etsySettings->get('cache_lifetime')));
return $data;
}
}
}
else {
return $this->call("shops/{$shopId}/sections");
}
}
/**
* {@inheritDoc}
*/
public function getListingProperties($listingId): mixed {
$shopId = $this->etsySettings->get('shop_id');
$data = NULL;
if ($this->etsySettings->get('cache_lifetime') > 0) {
$cid = 'etsy:listing_properties:' . $listingId;
if ($cache = $this->cache->get($cid)) {
$data = $cache->data;
} else {
$response = $this->call("shops/{$shopId}/listings/{$listingId}/properties}");
if (!isset($response->error)) {
$data = $response;
$this->cache->set($cid, $data, (time() + $this->etsySettings->get('cache_lifetime')));
}
}
}
else {
$data = $this->call("shops/{$shopId}/listings/{$listingId}/properties");
}
return $data;
}
/**
* {@inheritDoc}
*/
function getTaxonomy(string $type, int $taxonomy_id = 0 ): object|bool {
if ( $type !== 'buyer' && $type !== 'seller' ) {
throw new \Exception('Invalid value for $type. Valid types are "buyer", "seller".');
}
$shopId = $this->etsySettings->get('shop_id');
$uri = $type . '-taxonomy/nodes';
if( !empty($taxonomy_id)) {
$uri .= "/{$taxonomy_id}/properties";
}
return $this->call($uri, 'GET');
}
/**
* {@inheritDoc}
*/
public function getShopReceipts(int $receiptId = NULL, array $params = []): object|bool {
$shopId = $this->etsySettings->get('shop_id');
$uri = 'shops/' . $shopId . '/receipts';
if( !empty($receiptId) ) {
$uri .= '/' . $receiptId;
}
return $this->call($uri, 'GET', $params);
}
/**
* {@inheritDoc}
*/
public function getListingTransactions(int $listingId): object|bool {
$shopId = $this->etsySettings->get('shop_id');
$uri = "shops/{$shopId}/listings/{$listingId}/transactions";
return $this->call($uri, 'GET');
}
/**
* Utility function that makes all the API calls.
*
* @param string $uri
* @param string $method
* @param array $params
*
* @return false|mixed
*/
protected function call($uri, $method = 'GET', $params = []) {
$uri = self::ENDPOINT . $uri;
$client = $this->oauthClient->getClient('etsy');
$access_token = $client->getAccessToken();
$headers = [
'x-api-key' => $client->getClientId(),
'Authorization' => 'Bearer ' . $access_token->getToken(),
];
$options = [
'headers' => $headers,
];
switch ($method) {
case 'GET':
$options['query'] = $params;
try {
$response = $this->httpClient->request('GET', $uri, $options);
return json_decode($response->getBody()->getContents());
} catch (\Exception $e) {
$errorStr = <<<ERRORSTR
<strong>Etsy API Error</strong><br />
<br />
Error code: <em>{$e->getCode()}</em><br />
<br />
Error message:<br />
{$e->getMessage()}<br />
<br />
Stack trace:<br />
{$e->getTraceAsString()}
ERRORSTR;
$response = new \stdClass();
$response->error = $e->getMessage();
return $response;
}
break;
default:
throw new \Exception('Etsy API method ' . $method . ' not implemented.');
}
}
}
