From 8da242b0603fbfd22abbce063eb7d854b74ed656 Mon Sep 17 00:00:00 2001 From: "David T. Sadler" Date: Tue, 7 Mar 2017 12:46:19 +0000 Subject: [PATCH] 10.0.0 release close #68 close #75 --- CHANGELOG.md | 10 + docs/guide/configuration.rst | 11 +- docs/guide/restful-services.rst | 132 +++++ src/OAuth/Services/OAuthService.php | 430 +++++++++++++++++ src/OAuth/Types/GetAppTokenRestRequest.php | 51 ++ src/OAuth/Types/GetAppTokenRestResponse.php | 91 ++++ src/OAuth/Types/GetUserTokenRestRequest.php | 51 ++ src/OAuth/Types/GetUserTokenRestResponse.php | 98 ++++ .../Types/RefreshUserTokenRestRequest.php | 51 ++ .../Types/RefreshUserTokenRestResponse.php | 91 ++++ src/Sdk.php | 2 +- src/Trading/Enums/PictureFormatCodeType.php | 1 + src/Trading/Services/TradingService.php | 2 +- .../Types/CalculatedShippingRateType.php | 42 -- src/Trading/Types/ItemType.php | 56 --- .../ListingCheckoutRedirectPreferenceType.php | 14 - src/Trading/Types/PictureDetailsType.php | 7 - .../Types/ProductListingDetailsType.php | 7 + src/Trading/Types/ReturnPolicyType.php | 7 - src/Trading/Types/ShippingDetailsType.php | 14 - src/Trading/Types/ShippingPackageInfoType.php | 21 + .../VariationProductListingDetailsType.php | 7 + test/Mocks/HttpOAuthHandler.php | 33 ++ test/Mocks/OAuthRestResponse.json | 6 + test/OAuth/Services/ServiceTest.php | 450 ++++++++++++++++++ .../Types/GetAppTokenRestRequestTest.php | 35 ++ .../Types/GetAppTokenRestResponseTest.php | 43 ++ .../Types/GetUserTokenRestRequestTest.php | 35 ++ .../Types/GetUserTokenRestResponseTest.php | 45 ++ .../Types/RefreshUserTokenRestRequestTest.php | 35 ++ .../RefreshUserTokenRestResponseTest.php | 43 ++ test/SDKTest.php | 6 + 32 files changed, 1784 insertions(+), 143 deletions(-) create mode 100644 src/OAuth/Services/OAuthService.php create mode 100644 src/OAuth/Types/GetAppTokenRestRequest.php create mode 100644 src/OAuth/Types/GetAppTokenRestResponse.php create mode 100644 src/OAuth/Types/GetUserTokenRestRequest.php create mode 100644 src/OAuth/Types/GetUserTokenRestResponse.php create mode 100644 src/OAuth/Types/RefreshUserTokenRestRequest.php create mode 100644 src/OAuth/Types/RefreshUserTokenRestResponse.php create mode 100644 test/Mocks/HttpOAuthHandler.php create mode 100644 test/Mocks/OAuthRestResponse.json create mode 100644 test/OAuth/Services/ServiceTest.php create mode 100644 test/OAuth/Types/GetAppTokenRestRequestTest.php create mode 100644 test/OAuth/Types/GetAppTokenRestResponseTest.php create mode 100644 test/OAuth/Types/GetUserTokenRestRequestTest.php create mode 100644 test/OAuth/Types/GetUserTokenRestResponseTest.php create mode 100644 test/OAuth/Types/RefreshUserTokenRestRequestTest.php create mode 100644 test/OAuth/Types/RefreshUserTokenRestResponseTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0170c7284..c791ff373 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +## 10.0.0 - 2017-03-17 + +### Breaking changes + +* Support Trading API version 997. + +## Features + +* Can now use SDK to handle generation of OAUTH tokens for the RESTFul services. + ## 9.0.1 - 2017-03-06 ### Fixes diff --git a/docs/guide/configuration.rst b/docs/guide/configuration.rst index 1af372392..4cead353d 100644 --- a/docs/guide/configuration.rst +++ b/docs/guide/configuration.rst @@ -118,7 +118,7 @@ credentials ~~~~~~~~~~~ :Type: ``array|DTS\eBaySDK\Credentials\CredentialsInterface|callable`` -:Services: ``BulkDataExchange``, ``BusinessPoliciesManagement``, ``Feedback``, ``FileTransfer``, ``Finding``, ``HalfFinding``, ``Merchandising``, ``Product``, ``ProductMetadata``, ``RelatedItemsManagement``, ``ResolutionCaseManagement``, ``ReturnManagement``, ``Shopping``, ``Trading``. +:Services: ``BulkDataExchange``, ``BusinessPoliciesManagement``, ``Feedback``, ``FileTransfer``, ``Finding``, ``HalfFinding``, ``Merchandising``, ``OAuth``, ``Product``, ``ProductMetadata``, ``RelatedItemsManagement``, ``ResolutionCaseManagement``, ``ReturnManagement``, ``Shopping``, ``Trading``. Provide your "Application ID", "Certificate ID", and "Developer ID" credentials that are required when using the eBay API. If you do not provide any credentials the SDK will attempt to load them in the following order: @@ -440,6 +440,15 @@ responseLanguage This configuration option will set the ``Accept-Language`` HTTP header for the request. +ruName +~~~~~~~~~~~~~~~~ + +:Type: ``string`` +:Services: ``OAuth`` +:Required: ``true`` + +This is the eBay Redirect URL name. eBay assigns two unique RuName values to your application, one for the Sandbox and another for the Production environment. + sandbox ~~~~~~~ diff --git a/docs/guide/restful-services.rst b/docs/guide/restful-services.rst index d057b6d1a..0f2f840f7 100644 --- a/docs/guide/restful-services.rst +++ b/docs/guide/restful-services.rst @@ -140,3 +140,135 @@ Returns true if any header names match the given header name using a case-insens ); } +OAuth access tokens +------------------- + +The SDK provides some support for generating the OAuth tokens needed by the RESTful services. This is provided via the ``\DTS\eBaySDK\OAuth\Services\OAuthService`` class. + + +.. code-block:: php + + use \DTS\eBaySDK\OAuth\Services; + use \DTS\eBaySDK\OAuth\Types; + + $service = new Services\OAuthService([ + 'credentials' => '', + 'ruName' => '' + ]); + +Application tokens +~~~~~~~~~~~~~~~~~~ + +An application token can be generated by calling the ``getAppToken`` method on the service object. + +.. code-block:: php + + $response = $service->getAppToken(); + + printf("\nStatus Code: %s\n\n", $response->getStatusCode()); + if ($response->getStatusCode() !== 200) { + printf( + "%s: %s\n\n", + $response->error, + $response->error_description + ); + } else { + printf( + "%s\n%s\n%s\n\n", + $response->access_token, + $response->token_type, + $response->expires_in + ); + } + +User tokens +~~~~~~~~~~~ + +Generating a user token requires your application to redirect a user to eBay where they will grant permission. The redirect url can be created via the ``redirectUrlForUser`` method. + +.. code-block:: php + + $service = new Services\OAuthService([ + 'credentials' => [ + 'appId' => '111', + 'certId' => '222', + 'devId' => '333', + ], + 'ruName' => 'foo' + ]); + + $url = $service->redirectUrlForUser([ + 'state' => 'bar', + 'scope' => [ + 'https://api.ebay.com/oauth/api_scope/sell.account', + 'https://api.ebay.com/oauth/api_scope/sell.inventory' + ] + ]); + + echo $url; + /** + * Outputs (wrapped for readability) + * + * https://signin.ebay.com/authorize? + * client_id=111& + * redirect_uri=foo& + * response_type=code& + * state=bar& + * scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.account%20 + * https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.inventory + */ + +Once a user has granted permission your application will be given a code that should be exchanged for an oauth token. This can be done with the ``getUserToken`` method. + + +.. code-block:: php + + $response = $service->getUserToken(new Types\GetUserTokenRestRequest([ + 'code' => '' + ])); + + printf("\nStatus Code: %s\n\n", $response->getStatusCode()); + if ($response->getStatusCode() !== 200) { + printf( + "%s: %s\n\n", + $response->error, + $response->error_description + ); + } else { + printf( + "%s\n%s\n%s\n%s\n\n", + $response->access_token, + $response->token_type, + $response->expires_in, + $response->refresh_token + ); + } + +The oauth tokens that eBay generate are short lived. A refresh token is given to your application in order to generate a new token without the need for prompting the user. The SDK provides the ``refreshUserToken`` method to handle this process. When calling this method you must ensure that the same ``scope`` values used in the ``redirectUrlForUser`` method is used. + +.. code-block:: php + + $response = $service->refreshUserToken(new Types\RefreshUserTokenRestRequest([ + 'refresh_token' => '', + 'scope' => [ + 'https://api.ebay.com/oauth/api_scope/sell.account', + 'https://api.ebay.com/oauth/api_scope/sell.inventory' + ] + ])); + + printf("\nStatus Code: %s\n\n", $response->getStatusCode()); + if ($response->getStatusCode() !== 200) { + printf( + "%s: %s\n\n", + $response->error, + $response->error_description + ); + } else { + printf( + "%s\n%s\n%s\n%s\n\n", + $response->access_token, + $response->token_type, + $response->expires_in, + $response->refresh_token + ); + } diff --git a/src/OAuth/Services/OAuthService.php b/src/OAuth/Services/OAuthService.php new file mode 100644 index 000000000..d1560cc55 --- /dev/null +++ b/src/OAuth/Services/OAuthService.php @@ -0,0 +1,430 @@ + 'https://api.sandbox.ebay.com/identity', + 'production' => 'https://api.ebay.com/identity' + ]; + + /** + * @property array $operations Associative array of operations provided by the service. + */ + private static $operations = [ + 'getUserToken' => [ + 'method' => 'POST', + 'resource' => 'oauth2/token', + 'responseClass' => '\DTS\eBaySDK\OAuth\Types\GetUserTokenRestResponse', + 'params' => [ + ] + ], + 'refreshUserToken' => [ + 'method' => 'POST', + 'resource' => 'oauth2/token', + 'responseClass' => '\DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestResponse', + 'params' => [ + ] + ], + 'getAppToken' => [ + 'method' => 'POST', + 'resource' => 'oauth2/token', + 'responseClass' => '\DTS\eBaySDK\OAuth\Types\GetAppTokenRestResponse', + 'params' => [ + ] + ] + ]; + + /** + * @var \DTS\eBaySDK\ConfigurationResolver Resolves configuration options. + */ + private $resolver; + + /** + * @var \DTS\eBaySDK\UriResolver Resolves uri parameters. + */ + private $uriResolver; + + /** + * @var array Associative array storing the current configuration option values. + */ + private $config; + + /** + * @param array $config Configuration option values. + */ + public function __construct(array $config) + { + $this->resolver = new ConfigurationResolver(static::getConfigDefinitions()); + $this->uriResolver = new UriResolver(); + $this->config = $this->resolver->resolve($config); + } + + /** + * Returns definitions for each configuration option that is supported. + * + * @return array An associative array of configuration definitions. + */ + public static function getConfigDefinitions() + { + return [ + 'apiVersion' => [ + 'valid' => ['string'], + 'default' => \DTS\eBaySDK\OAuth\Services\OAuthService::API_VERSION, + 'required' => true + ], + 'profile' => [ + 'valid' => ['string'], + 'fn' => 'DTS\eBaySDK\applyProfile', + ], + 'credentials' => [ + 'valid' => ['DTS\eBaySDK\Credentials\CredentialsInterface', 'array', 'callable'], + 'fn' => 'DTS\eBaySDK\applyCredentials', + 'default' => [CredentialsProvider::class, 'defaultProvider'] + ], + 'debug' => [ + 'valid' => ['bool', 'array'], + 'fn' => 'DTS\eBaySDK\applyDebug', + 'default' => false + ], + 'httpHandler' => [ + 'valid' => ['callable'], + 'default' => 'DTS\eBaySDK\defaultHttpHandler' + ], + 'httpOptions' => [ + 'valid' => ['array'], + 'default' => [ + 'http_errors' => false + ] + ], + 'ruName' => [ + 'valid' => ['string'], + 'required' => true + ], + 'sandbox' => [ + 'valid' => ['bool'], + 'default' => false + ] + ]; + } + + /** + * Method to get the service's configuration. + * + * @param string|null $option The name of the option whos value will be returned. + * + * @return mixed Returns an associative array of configuration options if no parameters are passed, + * otherwise returns the value for the specified configuration option. + */ + public function getConfig($option = null) + { + return $option === null + ? $this->config + : (isset($this->config[$option]) + ? $this->config[$option] + : null); + } + + /** + * Set multiple configuration options. + * + * @param array $configuration Associative array of configuration options and their values. + */ + public function setConfig(array $configuration) + { + $this->config = Functions\arrayMergeDeep( + $this->config, + $this->resolver->resolveOptions($configuration) + ); + } + + /** + * Helper method to return the value of the credentials configuration option. + * + * @return \DTS\eBaySDK\Credentials\CredentialsInterface + */ + public function getCredentials() + { + return $this->getConfig('credentials'); + } + + /** + * @param array $params An associative array with state and scope as the keys. + * + * @return string The redirect URL. + * @throws \InvalidArgumentException. + */ + public function redirectUrlForUser(array $params) + { + if (!array_key_exists('state', $params)) { + throw new \InvalidArgumentException('state parameter required'); + } + + if (!array_key_exists('scope', $params)) { + throw new \InvalidArgumentException('scope parameter required'); + } + + $url = $this->getConfig('sandbox') + ? 'https://signin.sandbox.ebay.com/authorize?' + : 'https://signin.ebay.com/authorize?'; + + $urlParams = [ + 'client_id' => $this->getConfig('credentials')->getAppId(), + 'redirect_uri' => $this->getConfig('ruName'), + 'response_type' => 'code', + 'state' => $params['state'], + 'scope' => implode($params['scope'], ' ') + + ]; + + return $url.http_build_query($urlParams, null, '&', PHP_QUERY_RFC3986); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest $request + * @return \DTS\eBaySDK\OAuth\Types\GetUserTokenRestResponse + */ + public function getUserToken(\DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest $request) + { + return $this->getUserTokenAsync($request)->wait(); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest $request + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function getUserTokenAsync(\DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest $request) + { + if (!$request) { + $request = new \DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest(); + } + if (!isset($request->grant_type)) { + $request->grant_type = 'authorization_code'; + } + if (!isset($request->redirect_uri)) { + $request->redirect_uri = $this->getConfig('ruName'); + } + + return $this->callOperationAsync('getUserToken', $request); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestRequest $request + * @return \DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestResponse + */ + public function refreshUserToken(\DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestRequest $request) + { + return $this->refreshUserTokenAsync($request)->wait(); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestRequest $request + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function refreshUserTokenAsync(\DTS\eBaySDK\OAuth\Types\refreshUserTokenRestRequest $request) + { + if (!$request) { + $request = new \DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestRequest(); + } + if (!isset($request->grant_type)) { + $request->grant_type = 'refresh_token'; + } + + return $this->callOperationAsync('refreshUserToken', $request); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest $request + * @return \DTS\eBaySDK\OAuth\Types\GetAppTokenRestResponse + */ + public function getAppToken(\DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest $request = null) + { + return $this->getAppTokenAsync()->wait(); + } + + /** + * @param \DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest $request + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function getAppTokenAsync(\DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest $request = null) + { + if (!$request) { + $request = new \DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest(); + } + if (!isset($request->grant_type)) { + $request->grant_type = 'client_credentials'; + } + if (!isset($request->redirect_uri)) { + $request->redirect_uri = $this->getConfig('ruName'); + } + if (!isset($request->scope)) { + $request->scope = 'https://api.ebay.com/oauth/api_scope'; + } + + return $this->callOperationAsync('getAppToken', $request); + } + + /** + * Sends an asynchronous API request. + * + * @param string $name The name of the operation. + * @param \DTS\eBaySDK\Types\BaseType $request Request object containing the request information. + * + * @return \GuzzleHttp\Promise\PromiseInterface A promise that will be resolved with an object created from the JSON response. + */ + private function callOperationAsync($name, \DTS\eBaySDK\Types\BaseType $request = null) + { + $operation = static::$operations[$name]; + + $paramValues = []; + $requestValues = []; + + if ($request) { + $requestArray = $request->toArray(); + $paramValues = array_intersect_key($requestArray, $operation['params']); + $requestValues = array_diff_key($requestArray, $operation['params']); + } + + $url = $this->uriResolver->resolve( + $this->getUrl(), + $this->getConfig('apiVersion'), + $operation['resource'], + $operation['params'], + $paramValues + ); + $method = $operation['method']; + $body = $this->buildRequestBody($requestValues); + $headers = $this->buildRequestHeaders($body); + $responseClass = $operation['responseClass']; + $debug = $this->getConfig('debug'); + $httpHandler = $this->getConfig('httpHandler'); + $httpOptions = $this->getConfig('httpOptions'); + + if ($debug !== false) { + $this->debugRequest($url, $headers, $body); + } + + $request = new Request($method, $url, $headers, $body); + + return $httpHandler($request, $httpOptions)->then( + function (ResponseInterface $res) use ($debug, $responseClass) { + $json = $res->getBody()->getContents(); + + if ($debug !== false) { + $this->debugResponse($json); + } + + return new $responseClass( + $json !== '' ? json_decode($json, true) : [], + $res->getStatusCode(), + $res->getHeaders() + ); + } + ); + } + + /** + * Helper function to return the URL as determined by the sandbox configuration option. + * + * @return string Either the production or sandbox URL. + */ + private function getUrl() + { + return $this->getConfig('sandbox') ? static::$endPoints['sandbox'] : static::$endPoints['production']; + } + + /** + * Builds the request body string. + * + * @param array $request Associative array that is the request body. + * + * @return string The request body in URL-encoded format. + */ + private function buildRequestBody(array $request) + { + $params = array_reduce(array_keys($request), function ($carry, $key) use($request) { + $value = $request[$key]; + $carry[$key] = is_array($value) ? implode(' ', $value) : $value; + return $carry; + }, []); + + return empty($request) ? '' : http_build_query($params, null, '&', PHP_QUERY_RFC3986); + } + + /** + * Helper function that builds the HTTP request headers. + * + * @param string $body The request body. + * + * @return array An associative array of HTTP headers. + */ + private function buildRequestHeaders($body) + { + $credentials = $this->getConfig('credentials'); + $appId = $credentials->getAppId(); + $certId = $credentials->getCertId(); + + $headers = []; + + $headers['Accept'] = 'application/json'; + $headers['Authorization'] = 'Basic '.base64_encode($appId.':'.$certId); + $headers['Content-Type'] = 'application/x-www-form-urlencoded'; + $headers['Content-Length'] = strlen($body); + + return $headers; + } + + /** + * Sends a debug string of the request details. + * + * @param string $url API endpoint. + * @param array $headers Associative array of HTTP headers. + * @param string $body The JSON body of the request. + */ + private function debugRequest($url, array $headers, $body) + { + $str = $url.PHP_EOL; + + $str .= array_reduce(array_keys($headers), function ($str, $key) use ($headers) { + $str .= $key.': '.$headers[$key].PHP_EOL; + return $str; + }, ''); + + $str .= $body; + + $this->debug($str); + } + + /** + * Sends a debug string of the response details. + * + * @param string $body The JSON body of the response. + */ + private function debugResponse($body) + { + $this->debug($body); + } + + /** + * Sends a debug string via the attach debugger. + * + * @param string $str The debug information. + */ + private function debug($str) + { + $debugger = $this->getConfig('debug'); + $debugger($str); + } +} diff --git a/src/OAuth/Types/GetAppTokenRestRequest.php b/src/OAuth/Types/GetAppTokenRestRequest.php new file mode 100644 index 000000000..8e2018ee8 --- /dev/null +++ b/src/OAuth/Types/GetAppTokenRestRequest.php @@ -0,0 +1,51 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'grant_type' + ], + 'redirect_uri' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'redirect_uri' + ], + 'scope' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'scope' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + */ + public function __construct(array $values = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + } +} diff --git a/src/OAuth/Types/GetAppTokenRestResponse.php b/src/OAuth/Types/GetAppTokenRestResponse.php new file mode 100644 index 000000000..c949b9881 --- /dev/null +++ b/src/OAuth/Types/GetAppTokenRestResponse.php @@ -0,0 +1,91 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'access_token' + ], + 'token_type' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'token_type' + ], + 'expires_in' => [ + 'type' => 'integer', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'expires_in' + ], + 'refresh_token' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'refresh_token' + ], + 'error' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error' + ], + 'error_description' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_description' + ], + 'error_uri' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_uri' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + * @param int $statusCode Status code + * @param array $headers HTTP Response headers. + */ + public function __construct(array $values = [], $statusCode = 200, array $headers = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + + $this->statusCode = (int)$statusCode; + + $this->setHeaders($headers); + } +} diff --git a/src/OAuth/Types/GetUserTokenRestRequest.php b/src/OAuth/Types/GetUserTokenRestRequest.php new file mode 100644 index 000000000..cbd950147 --- /dev/null +++ b/src/OAuth/Types/GetUserTokenRestRequest.php @@ -0,0 +1,51 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'grant_type' + ], + 'redirect_uri' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'redirect_uri' + ], + 'code' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'code' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + */ + public function __construct(array $values = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + } +} diff --git a/src/OAuth/Types/GetUserTokenRestResponse.php b/src/OAuth/Types/GetUserTokenRestResponse.php new file mode 100644 index 000000000..48a8d0bb8 --- /dev/null +++ b/src/OAuth/Types/GetUserTokenRestResponse.php @@ -0,0 +1,98 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'access_token' + ], + 'token_type' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'token_type' + ], + 'expires_in' => [ + 'type' => 'integer', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'expires_in' + ], + 'refresh_token' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'refresh_token' + ], + 'refresh_token_expires_in' => [ + 'type' => 'integer', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'refresh_token_expires_in' + ], + 'error' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error' + ], + 'error_description' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_description' + ], + 'error_uri' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_uri' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + * @param int $statusCode Status code + * @param array $headers HTTP Response headers. + */ + public function __construct(array $values = [], $statusCode = 200, array $headers = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + + $this->statusCode = (int)$statusCode; + + $this->setHeaders($headers); + } +} diff --git a/src/OAuth/Types/RefreshUserTokenRestRequest.php b/src/OAuth/Types/RefreshUserTokenRestRequest.php new file mode 100644 index 000000000..7b512426b --- /dev/null +++ b/src/OAuth/Types/RefreshUserTokenRestRequest.php @@ -0,0 +1,51 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'grant_type' + ], + 'refresh_token' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'redirect_uri' + ], + 'scope' => [ + 'type' => 'string', + 'repeatable' => true, + 'attribute' => false, + 'elementName' => 'code' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + */ + public function __construct(array $values = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + } +} diff --git a/src/OAuth/Types/RefreshUserTokenRestResponse.php b/src/OAuth/Types/RefreshUserTokenRestResponse.php new file mode 100644 index 000000000..daa8d710d --- /dev/null +++ b/src/OAuth/Types/RefreshUserTokenRestResponse.php @@ -0,0 +1,91 @@ + [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'access_token' + ], + 'token_type' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'token_type' + ], + 'expires_in' => [ + 'type' => 'integer', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'expires_in' + ], + 'refresh_token' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'refresh_token' + ], + 'error' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error' + ], + 'error_description' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_description' + ], + 'error_uri' => [ + 'type' => 'string', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'error_uri' + ] + ]; + + /** + * @param array $values Optional properties and values to assign to the object. + * @param int $statusCode Status code + * @param array $headers HTTP Response headers. + */ + public function __construct(array $values = [], $statusCode = 200, array $headers = []) + { + list($parentValues, $childValues) = self::getParentValues(self::$propertyTypes, $values); + + parent::__construct($parentValues); + + if (!array_key_exists(__CLASS__, self::$properties)) { + self::$properties[__CLASS__] = array_merge(self::$properties[get_parent_class()], self::$propertyTypes); + } + + $this->setValues(__CLASS__, $childValues); + + $this->statusCode = (int)$statusCode; + + $this->setHeaders($headers); + } +} diff --git a/src/Sdk.php b/src/Sdk.php index ed1cf1f92..9db4126b0 100644 --- a/src/Sdk.php +++ b/src/Sdk.php @@ -30,7 +30,7 @@ */ class Sdk { - const VERSION = '9.0.1'; + const VERSION = '10.0.0'; /** * @var bool Controls if the SDK should enforce strict types diff --git a/src/Trading/Enums/PictureFormatCodeType.php b/src/Trading/Enums/PictureFormatCodeType.php index 28f5e7b67..74e845ecb 100644 --- a/src/Trading/Enums/PictureFormatCodeType.php +++ b/src/Trading/Enums/PictureFormatCodeType.php @@ -15,4 +15,5 @@ class PictureFormatCodeType const C_CUSTOM_CODE = 'CustomCode'; const C_GIF = 'GIF'; const C_JPG = 'JPG'; + const C_PNG = 'PNG'; } diff --git a/src/Trading/Services/TradingService.php b/src/Trading/Services/TradingService.php index 732b0c7ff..7416eb1b6 100644 --- a/src/Trading/Services/TradingService.php +++ b/src/Trading/Services/TradingService.php @@ -12,7 +12,7 @@ class TradingService extends \DTS\eBaySDK\Trading\Services\TradingBaseService { - const API_VERSION = '993'; + const API_VERSION = '997'; /** * @param array $config Configuration option values. diff --git a/src/Trading/Types/CalculatedShippingRateType.php b/src/Trading/Types/CalculatedShippingRateType.php index 6ea9bdbee..0089c4339 100644 --- a/src/Trading/Types/CalculatedShippingRateType.php +++ b/src/Trading/Types/CalculatedShippingRateType.php @@ -14,14 +14,8 @@ * * @property string $OriginatingPostalCode * @property \DTS\eBaySDK\Trading\Enums\MeasurementSystemCodeType $MeasurementUnit - * @property \DTS\eBaySDK\Trading\Types\MeasureType $PackageDepth - * @property \DTS\eBaySDK\Trading\Types\MeasureType $PackageLength - * @property \DTS\eBaySDK\Trading\Types\MeasureType $PackageWidth * @property \DTS\eBaySDK\Trading\Types\AmountType $PackagingHandlingCosts * @property boolean $ShippingIrregular - * @property \DTS\eBaySDK\Trading\Enums\ShippingPackageCodeType $ShippingPackage - * @property \DTS\eBaySDK\Trading\Types\MeasureType $WeightMajor - * @property \DTS\eBaySDK\Trading\Types\MeasureType $WeightMinor * @property \DTS\eBaySDK\Trading\Types\AmountType $InternationalPackagingHandlingCosts */ class CalculatedShippingRateType extends \DTS\eBaySDK\Types\BaseType @@ -42,24 +36,6 @@ class CalculatedShippingRateType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'MeasurementUnit' ], - 'PackageDepth' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\MeasureType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'PackageDepth' - ], - 'PackageLength' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\MeasureType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'PackageLength' - ], - 'PackageWidth' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\MeasureType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'PackageWidth' - ], 'PackagingHandlingCosts' => [ 'type' => 'DTS\eBaySDK\Trading\Types\AmountType', 'repeatable' => false, @@ -72,24 +48,6 @@ class CalculatedShippingRateType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'ShippingIrregular' ], - 'ShippingPackage' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'ShippingPackage' - ], - 'WeightMajor' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\MeasureType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'WeightMajor' - ], - 'WeightMinor' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\MeasureType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'WeightMinor' - ], 'InternationalPackagingHandlingCosts' => [ 'type' => 'DTS\eBaySDK\Trading\Types\AmountType', 'repeatable' => false, diff --git a/src/Trading/Types/ItemType.php b/src/Trading/Types/ItemType.php index e0ffad532..0d11d7d3c 100644 --- a/src/Trading/Types/ItemType.php +++ b/src/Trading/Types/ItemType.php @@ -68,7 +68,6 @@ * @property boolean $DisableBuyerRequirements * @property \DTS\eBaySDK\Trading\Types\BestOfferDetailsType $BestOfferDetails * @property boolean $LocationDefaulted - * @property boolean $ThirdPartyCheckout * @property boolean $UseTaxTable * @property boolean $BuyerResponsibleForShipping * @property string $eBayNotes @@ -82,11 +81,6 @@ * @property string $SellerInventoryID * @property \DTS\eBaySDK\Trading\Types\PictureDetailsType $PictureDetails * @property integer $DispatchTimeMax - * @property boolean $SkypeEnabled - * @property string $SkypeID - * @property \DTS\eBaySDK\Trading\Enums\SkypeContactOptionCodeType[] $SkypeContactOption - * @property boolean $ThirdPartyCheckoutIntegration - * @property \DTS\eBaySDK\Trading\Types\ListingCheckoutRedirectPreferenceType $ListingCheckoutRedirectPreference * @property \DTS\eBaySDK\Trading\Types\AddressType $SellerContactDetails * @property integer $TotalQuestionCount * @property boolean $ProxyItem @@ -118,9 +112,7 @@ * @property string $TaxCategory * @property \DTS\eBaySDK\Trading\Enums\QuantityAvailableHintCodeType $QuantityAvailableHint * @property integer $QuantityThreshold - * @property boolean $PostCheckoutExperienceEnabled * @property \DTS\eBaySDK\Trading\Types\DiscountPriceInfoType $DiscountPriceInfo - * @property boolean $UseRecommendedProduct * @property string $SellerProvidedTitle * @property string $VIN * @property string $VRM @@ -493,12 +485,6 @@ class ItemType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'LocationDefaulted' ], - 'ThirdPartyCheckout' => [ - 'type' => 'boolean', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'ThirdPartyCheckout' - ], 'UseTaxTable' => [ 'type' => 'boolean', 'repeatable' => false, @@ -577,36 +563,6 @@ class ItemType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'DispatchTimeMax' ], - 'SkypeEnabled' => [ - 'type' => 'boolean', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'SkypeEnabled' - ], - 'SkypeID' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'SkypeID' - ], - 'SkypeContactOption' => [ - 'type' => 'string', - 'repeatable' => true, - 'attribute' => false, - 'elementName' => 'SkypeContactOption' - ], - 'ThirdPartyCheckoutIntegration' => [ - 'type' => 'boolean', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'ThirdPartyCheckoutIntegration' - ], - 'ListingCheckoutRedirectPreference' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\ListingCheckoutRedirectPreferenceType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'ListingCheckoutRedirectPreference' - ], 'SellerContactDetails' => [ 'type' => 'DTS\eBaySDK\Trading\Types\AddressType', 'repeatable' => false, @@ -793,24 +749,12 @@ class ItemType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'QuantityThreshold' ], - 'PostCheckoutExperienceEnabled' => [ - 'type' => 'boolean', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'PostCheckoutExperienceEnabled' - ], 'DiscountPriceInfo' => [ 'type' => 'DTS\eBaySDK\Trading\Types\DiscountPriceInfoType', 'repeatable' => false, 'attribute' => false, 'elementName' => 'DiscountPriceInfo' ], - 'UseRecommendedProduct' => [ - 'type' => 'boolean', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'UseRecommendedProduct' - ], 'SellerProvidedTitle' => [ 'type' => 'string', 'repeatable' => false, diff --git a/src/Trading/Types/ListingCheckoutRedirectPreferenceType.php b/src/Trading/Types/ListingCheckoutRedirectPreferenceType.php index 7a78fe589..39e0d6f40 100644 --- a/src/Trading/Types/ListingCheckoutRedirectPreferenceType.php +++ b/src/Trading/Types/ListingCheckoutRedirectPreferenceType.php @@ -12,8 +12,6 @@ /** * - * @property string $ProStoresStoreName - * @property string $SellerThirdPartyUsername */ class ListingCheckoutRedirectPreferenceType extends \DTS\eBaySDK\Types\BaseType { @@ -21,18 +19,6 @@ class ListingCheckoutRedirectPreferenceType extends \DTS\eBaySDK\Types\BaseType * @var array Properties belonging to objects of this class. */ private static $propertyTypes = [ - 'ProStoresStoreName' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'ProStoresStoreName' - ], - 'SellerThirdPartyUsername' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'SellerThirdPartyUsername' - ] ]; /** diff --git a/src/Trading/Types/PictureDetailsType.php b/src/Trading/Types/PictureDetailsType.php index 836f9d24c..706e873b5 100644 --- a/src/Trading/Types/PictureDetailsType.php +++ b/src/Trading/Types/PictureDetailsType.php @@ -13,7 +13,6 @@ /** * * @property \DTS\eBaySDK\Trading\Enums\GalleryTypeCodeType $GalleryType - * @property string $GalleryURL * @property \DTS\eBaySDK\Trading\Enums\PhotoDisplayCodeType $PhotoDisplay * @property string[] $PictureURL * @property \DTS\eBaySDK\Trading\Enums\PictureSourceCodeType $PictureSource @@ -35,12 +34,6 @@ class PictureDetailsType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'GalleryType' ], - 'GalleryURL' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'GalleryURL' - ], 'PhotoDisplay' => [ 'type' => 'string', 'repeatable' => false, diff --git a/src/Trading/Types/ProductListingDetailsType.php b/src/Trading/Types/ProductListingDetailsType.php index 5521cebb2..6aa32ad26 100644 --- a/src/Trading/Types/ProductListingDetailsType.php +++ b/src/Trading/Types/ProductListingDetailsType.php @@ -25,6 +25,7 @@ * @property \DTS\eBaySDK\Trading\Types\TicketListingDetailsType $TicketListingDetails * @property boolean $UseFirstProduct * @property boolean $IncludeeBayProductDetails + * @property \DTS\eBaySDK\Trading\Types\NameValueListType[] $NameValueList */ class ProductListingDetailsType extends \DTS\eBaySDK\Types\BaseType { @@ -109,6 +110,12 @@ class ProductListingDetailsType extends \DTS\eBaySDK\Types\BaseType 'repeatable' => false, 'attribute' => false, 'elementName' => 'IncludeeBayProductDetails' + ], + 'NameValueList' => [ + 'type' => 'DTS\eBaySDK\Trading\Types\NameValueListType', + 'repeatable' => true, + 'attribute' => false, + 'elementName' => 'NameValueList' ] ]; diff --git a/src/Trading/Types/ReturnPolicyType.php b/src/Trading/Types/ReturnPolicyType.php index f4c1a37eb..ca3b0568e 100644 --- a/src/Trading/Types/ReturnPolicyType.php +++ b/src/Trading/Types/ReturnPolicyType.php @@ -25,7 +25,6 @@ * @property string $WarrantyType * @property string $WarrantyDurationOption * @property string $WarrantyDuration - * @property string $EAN * @property string $ShippingCostPaidByOption * @property string $ShippingCostPaidBy * @property string $RestockingFeeValue @@ -116,12 +115,6 @@ class ReturnPolicyType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'WarrantyDuration' ], - 'EAN' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'EAN' - ], 'ShippingCostPaidByOption' => [ 'type' => 'string', 'repeatable' => false, diff --git a/src/Trading/Types/ShippingDetailsType.php b/src/Trading/Types/ShippingDetailsType.php index 9fe93e450..91c1d1053 100644 --- a/src/Trading/Types/ShippingDetailsType.php +++ b/src/Trading/Types/ShippingDetailsType.php @@ -15,8 +15,6 @@ * @property boolean $GlobalShipping * @property \DTS\eBaySDK\Trading\Types\CalculatedShippingRateType $CalculatedShippingRate * @property boolean $ChangePaymentInstructions - * @property \DTS\eBaySDK\Trading\Types\AmountType $InsuranceFee - * @property \DTS\eBaySDK\Trading\Enums\InsuranceOptionCodeType $InsuranceOption * @property boolean $InsuranceWanted * @property boolean $PaymentEdited * @property string $PaymentInstructions @@ -71,18 +69,6 @@ class ShippingDetailsType extends \DTS\eBaySDK\Types\BaseType 'attribute' => false, 'elementName' => 'ChangePaymentInstructions' ], - 'InsuranceFee' => [ - 'type' => 'DTS\eBaySDK\Trading\Types\AmountType', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'InsuranceFee' - ], - 'InsuranceOption' => [ - 'type' => 'string', - 'repeatable' => false, - 'attribute' => false, - 'elementName' => 'InsuranceOption' - ], 'InsuranceWanted' => [ 'type' => 'boolean', 'repeatable' => false, diff --git a/src/Trading/Types/ShippingPackageInfoType.php b/src/Trading/Types/ShippingPackageInfoType.php index fedfa1d1d..6048e3d88 100644 --- a/src/Trading/Types/ShippingPackageInfoType.php +++ b/src/Trading/Types/ShippingPackageInfoType.php @@ -19,6 +19,9 @@ * @property \DateTime $ActualDeliveryTime * @property \DateTime $EstimatedDeliveryTimeMin * @property \DateTime $EstimatedDeliveryTimeMax + * @property \DateTime $HandleByTime + * @property \DateTime $MinNativeEstimatedDeliveryTime + * @property \DateTime $MaxNativeEstimatedDeliveryTime */ class ShippingPackageInfoType extends \DTS\eBaySDK\Types\BaseType { @@ -67,6 +70,24 @@ class ShippingPackageInfoType extends \DTS\eBaySDK\Types\BaseType 'repeatable' => false, 'attribute' => false, 'elementName' => 'EstimatedDeliveryTimeMax' + ], + 'HandleByTime' => [ + 'type' => 'DateTime', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'HandleByTime' + ], + 'MinNativeEstimatedDeliveryTime' => [ + 'type' => 'DateTime', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'MinNativeEstimatedDeliveryTime' + ], + 'MaxNativeEstimatedDeliveryTime' => [ + 'type' => 'DateTime', + 'repeatable' => false, + 'attribute' => false, + 'elementName' => 'MaxNativeEstimatedDeliveryTime' ] ]; diff --git a/src/Trading/Types/VariationProductListingDetailsType.php b/src/Trading/Types/VariationProductListingDetailsType.php index 57d6029d9..7eb110cf5 100644 --- a/src/Trading/Types/VariationProductListingDetailsType.php +++ b/src/Trading/Types/VariationProductListingDetailsType.php @@ -15,6 +15,7 @@ * @property string $ISBN * @property string $UPC * @property string $EAN + * @property \DTS\eBaySDK\Trading\Types\NameValueListType[] $NameValueList */ class VariationProductListingDetailsType extends \DTS\eBaySDK\Types\BaseType { @@ -39,6 +40,12 @@ class VariationProductListingDetailsType extends \DTS\eBaySDK\Types\BaseType 'repeatable' => false, 'attribute' => false, 'elementName' => 'EAN' + ], + 'NameValueList' => [ + 'type' => 'DTS\eBaySDK\Trading\Types\NameValueListType', + 'repeatable' => true, + 'attribute' => false, + 'elementName' => 'NameValueList' ] ]; diff --git a/test/Mocks/HttpOAuthHandler.php b/test/Mocks/HttpOAuthHandler.php new file mode 100644 index 000000000..56e865caa --- /dev/null +++ b/test/Mocks/HttpOAuthHandler.php @@ -0,0 +1,33 @@ +url = $request->getUri(); + $this->headers = []; + foreach ($request->getHeaders() as $name => $values) { + $this->headers[$name] = implode(', ', $values); + } + $this->body = $request->getBody(); + + // Return a fake json response. + $json = file_get_contents(__DIR__.'/../Mocks/OAuthRestResponse.json'); + + return new FulfilledPromise(new Response(200, [], Psr7\stream_for($json))); + } +} diff --git a/test/Mocks/OAuthRestResponse.json b/test/Mocks/OAuthRestResponse.json new file mode 100644 index 000000000..dc196bf1b --- /dev/null +++ b/test/Mocks/OAuthRestResponse.json @@ -0,0 +1,6 @@ +{ + "access_token":"foo", + "token_type":"bar", + "expires_in":123, + "refresh_token":"baz" +} diff --git a/test/OAuth/Services/ServiceTest.php b/test/OAuth/Services/ServiceTest.php new file mode 100644 index 000000000..a478cb937 --- /dev/null +++ b/test/OAuth/Services/ServiceTest.php @@ -0,0 +1,450 @@ +assertArrayHasKey('credentials', $d); + $this->assertEquals([ + 'valid' => ['DTS\eBaySDK\Credentials\CredentialsInterface', 'array', 'callable'], + 'fn' => 'DTS\eBaySDK\applyCredentials', + 'default' => [CredentialsProvider::class, 'defaultProvider'] + ], $d['credentials']); + + $this->assertArrayHasKey('debug', $d); + $this->assertEquals([ + 'valid' => ['bool', 'array'], + 'fn' => 'DTS\eBaySDK\applyDebug', + 'default' => false + ], $d['debug']); + + $this->assertArrayHasKey('httpHandler', $d); + $this->assertEquals([ + 'valid' => ['callable'], + 'default' => 'DTS\eBaySDK\defaultHttpHandler' + ], $d['httpHandler']); + + $this->assertArrayHasKey('httpOptions', $d); + $this->assertEquals([ + 'valid' => ['array'], + 'default' => ['http_errors' => false] + ], $d['httpOptions']); + + $this->assertArrayHasKey('profile', $d); + $this->assertEquals([ + 'valid' => ['string'], + 'fn' => 'DTS\eBaySDK\applyProfile', + ], $d['profile']); + + $this->assertArrayHasKey('ruName', $d); + $this->assertEquals([ + 'valid' => ['string'], + 'required' => true + ], $d['ruName']); + + $this->assertArrayHasKey('sandbox', $d); + $this->assertEquals([ + 'valid' => ['bool'], + 'default' => false + ], $d['sandbox']); + } + + public function testSandboxRedirectUrlForUser() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'baz', + 'sandbox' => true + ]); + $url = 'https://signin.sandbox.ebay.com/authorize?client_id=foo&redirect_uri=baz&response_type=code&state=111&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.account%20https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.inventory'; + + $this->assertEquals($url, $s->redirectUrlForUser([ + 'state' => '111', + 'scope' => [ + 'https://api.ebay.com/oauth/api_scope/sell.account', + 'https://api.ebay.com/oauth/api_scope/sell.inventory' + ] + ])); + } + + public function testProductionRedirectUrlForUser() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'baz' + ]); + $url = 'https://signin.ebay.com/authorize?client_id=foo&redirect_uri=baz&response_type=code&state=111&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.account%20https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fsell.inventory'; + + $this->assertEquals($url, $s->redirectUrlForUser([ + 'state' => '111', + 'scope' => [ + 'https://api.ebay.com/oauth/api_scope/sell.account', + 'https://api.ebay.com/oauth/api_scope/sell.inventory' + ] + ])); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage state parameter + */ + public function testExceptionThrowForMissingStateParam() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'baz' + ]); + + $s->redirectUrlForUser([ + 'scope' => [] + ]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage scope parameter + */ + public function testExceptionThrowForMissingScopeParam() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'baz' + ]); + + $s->redirectUrlForUser([ + 'state' => '' + ]); + } + public function testProductionUrlIsUsed() + { + // By default sandbox will be false. + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => '', + 'certId' => '', + 'devId' => '' + ], + 'ruName' => 'foo', + 'httpHandler' => $h + ]); + $s->getAppToken(); + + $this->assertEquals('https://api.ebay.com/identity/v1/oauth2/token', $h->url); + } + + public function testSandboxUrlIsUsed() + { + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => '', + 'certId' => '', + 'devId' => '' + ], + 'ruName' => 'foo', + 'sandbox' => true, + 'httpHandler' => $h + ]); + $s->getAppToken(); + + $this->assertEquals('https://api.sandbox.ebay.com/identity/v1/oauth2/token', $h->url); + } + + public function testHttpHeadersAreCreated() + { + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'foo', + 'httpHandler' => $h + ]); + $s->getAppToken(); + + $this->assertArrayHasKey('Accept', $h->headers); + $this->assertEquals('application/json', $h->headers['Accept']); + $this->assertArrayHasKey('Authorization', $h->headers); + $this->assertEquals('Basic '.base64_encode('foo:bar'), $h->headers['Authorization']); + $this->assertArrayHasKey('Content-Type', $h->headers); + $this->assertEquals('application/x-www-form-urlencoded', $h->headers['Content-Type']); + $this->assertArrayHasKey('Content-Length', $h->headers); + $this->assertEquals(strlen(http_build_query([ + 'grant_type' => 'client_credentials', + 'redirect_uri' => 'foo', + 'scope' => 'https://api.ebay.com/oauth/api_scope' + ])), $h->headers['Content-Length']); + } + + public function testBodyIsCreated() + { + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'foo', + 'httpHandler' => $h + ]); + $s->getAppToken(); + $this->assertEquals(http_build_query([ + 'grant_type' => 'client_credentials', + 'redirect_uri' => 'foo', + 'scope' => 'https://api.ebay.com/oauth/api_scope' + ]), $h->body); + } + + public function testResponseIsReturned() + { + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'foo', + 'httpHandler' => $h + ]); + $r = $s->getAppToken(); + + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\GetAppTokenRestResponse', $r); + $this->assertEquals('foo', $r->access_token); + $this->assertEquals('bar', $r->token_type); + $this->assertEquals(123, $r->expires_in); + $this->assertEquals('baz', $r->refresh_token); + } + + public function testDebugging() + { + $str = ''; + $logfn = function ($value) use (&$str) { + $str .= $value; + }; + $body = http_build_query([ + 'grant_type' => 'client_credentials', + 'redirect_uri' => 'foo', + 'scope' => 'https://api.ebay.com/oauth/api_scope' + ]); + + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'foo', + 'debug' => ['logfn' => $logfn], + 'httpHandler' => new HttpOAuthHandler() + ]); + $r = $s->getAppToken(); + + $this->assertContains('Content-Type: application/x-www-form-urlencoded', $str); + $this->assertContains('Content-Length: '.strlen($body), $str); + $this->assertContains('foo', $str); + $this->assertContains('bar', $str); + } + + public function testCredentialsInstanceCanBePassed() + { + $s = new OAuthService([ + 'credentials' => new Credentials('111', '222', '333'), + 'ruName' => 'foo' + ]); + + $c = $s->getCredentials(); + $this->assertEquals('111', $c->getAppId()); + $this->assertEquals('222', $c->getCertId()); + $this->assertEquals('333', $c->getDevId()); + } + + public function testCredentialsCanBeHardCoded() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => '111', + 'certId' => '222', + 'devId' => '333' + ], + 'ruName' => 'foo' + ]); + + $c = $s->getCredentials(); + $this->assertEquals('111', $c->getAppId()); + $this->assertEquals('222', $c->getCertId()); + $this->assertEquals('333', $c->getDevId()); + } + + public function testCredentialsCanBeProvided() + { + $s = new OAuthService([ + 'credentials' => function () { + return new Credentials('111', '222', '333'); + }, + 'ruName' => 'foo' + ]); + + $c = $s->getCredentials(); + $this->assertEquals('111', $c->getAppId()); + $this->assertEquals('222', $c->getCertId()); + $this->assertEquals('333', $c->getDevId()); + } + + public function testCredentialsCanBeLoadedFromIni() + { + $ini = <<clearEnv(); + file_put_contents($dir . '/credentials', $ini); + putenv('HOME=' . dirname($dir)); + + $s = new OAuthService([ + 'profile' => 'foo', + 'ruName' => 'foo' + ]); + $c = $s->getCredentials(); + + $this->assertEquals('111', $c->getAppId()); + $this->assertEquals('222', $c->getCertId()); + $this->assertEquals('333', $c->getDevId()); + + unlink($dir . '/credentials'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No credentials present in INI profile + */ + public function testCredentialsIniWillThrowException() + { + $ini = <<clearEnv(); + file_put_contents($dir . '/credentials', $ini); + putenv('HOME=' . dirname($dir)); + + $s = new OAuthService([ + 'profile' => 'foo', + 'ruName' => 'foo' + ]); + + try { + $s->getCredentials(); + } catch (\Exception $e) { + unlink($dir . '/credentials'); + throw $e; + } + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cannot locate credentials + */ + public function testCredentialsProviderThrowsIfCantProvide() + { + new OAuthService([ + 'credentials' => function () { + return new \InvalidArgumentException('Cannot locate credentials'); + }, + 'ruName' => 'foo' + ]); + } + + public function testCanSetConfigurationOptionsAfterInstaniation() + { + $h = new HttpOAuthHandler(); + $s = new OAuthService([ + 'credentials' => [ + 'appId' => 'foo', + 'certId' => 'bar', + 'devId' => '' + ], + 'ruName' => 'foo', + 'sandbox' => true, + 'httpHandler' => $h, + 'httpOptions' => [] + ]); + + $this->assertEquals([ + 'apiVersion' => 'v1', + 'credentials' => new Credentials('foo', 'bar', ''), + 'ruName' => 'foo', + 'sandbox' => true, + 'debug' => false, + 'httpHandler' => $h, + 'httpOptions' => [] + ], $s->getConfig()); + + $s->setConfig([ + 'sandbox' => false, + ]); + + $this->assertEquals([ + 'apiVersion' => 'v1', + 'credentials' => new Credentials('foo', 'bar', ''), + 'ruName' => 'foo', + 'sandbox' => false, + 'debug' => false, + 'httpHandler' => $h, + 'httpOptions' => [] + ], $s->getConfig()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid configuration value provided for "sandbox". Expected bool, but got int(-1) + */ + public function testSetConfigWillThrow() + { + $s = new OAuthService([ + 'credentials' => [ + 'appId' => '', + 'certId' => '', + 'devId' => '' + ], + 'ruName' => 'foo', + 'x' => 1 + ]); + + $s->setConfig(['sandbox' => -1]); + } +} diff --git a/test/OAuth/Types/GetAppTokenRestRequestTest.php b/test/OAuth/Types/GetAppTokenRestRequestTest.php new file mode 100644 index 000000000..eaea46837 --- /dev/null +++ b/test/OAuth/Types/GetAppTokenRestRequestTest.php @@ -0,0 +1,35 @@ +obj = new GetAppTokenRestRequest(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\GetAppTokenRestRequest', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->grant_type = 'foo'; + $this->obj->redirect_uri = 'bar'; + $this->obj->scope = 'baz'; + + $this->assertEquals('foo', $this->obj->grant_type); + $this->assertEquals('bar', $this->obj->redirect_uri); + $this->assertEquals('baz', $this->obj->scope); + } +} diff --git a/test/OAuth/Types/GetAppTokenRestResponseTest.php b/test/OAuth/Types/GetAppTokenRestResponseTest.php new file mode 100644 index 000000000..98758d78d --- /dev/null +++ b/test/OAuth/Types/GetAppTokenRestResponseTest.php @@ -0,0 +1,43 @@ +obj = new GetAppTokenRestResponse(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\GetAppTokenRestResponse', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->access_token = 'foo'; + $this->obj->token_type = 'bar'; + $this->obj->expires_in = 123; + $this->obj->refresh_token = 'baz'; + $this->obj->error = 'foo'; + $this->obj->error_description = 'bar'; + $this->obj->error_uri = 'baz'; + + $this->assertEquals('foo', $this->obj->access_token); + $this->assertEquals('bar', $this->obj->token_type); + $this->assertEquals(123, $this->obj->expires_in); + $this->assertEquals('baz', $this->obj->refresh_token); + $this->assertEquals('foo', $this->obj->error); + $this->assertEquals('bar', $this->obj->error_description); + $this->assertEquals('baz', $this->obj->error_uri); + } +} diff --git a/test/OAuth/Types/GetUserTokenRestRequestTest.php b/test/OAuth/Types/GetUserTokenRestRequestTest.php new file mode 100644 index 000000000..8bec38150 --- /dev/null +++ b/test/OAuth/Types/GetUserTokenRestRequestTest.php @@ -0,0 +1,35 @@ +obj = new GetUserTokenRestRequest(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\GetUserTokenRestRequest', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->grant_type = 'foo'; + $this->obj->redirect_uri = 'bar'; + $this->obj->code = 'baz'; + + $this->assertEquals('foo', $this->obj->grant_type); + $this->assertEquals('bar', $this->obj->redirect_uri); + $this->assertEquals('baz', $this->obj->code); + } +} diff --git a/test/OAuth/Types/GetUserTokenRestResponseTest.php b/test/OAuth/Types/GetUserTokenRestResponseTest.php new file mode 100644 index 000000000..38422f601 --- /dev/null +++ b/test/OAuth/Types/GetUserTokenRestResponseTest.php @@ -0,0 +1,45 @@ +obj = new GetUserTokenRestResponse(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\GetUserTokenRestResponse', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->access_token = 'foo'; + $this->obj->token_type = 'bar'; + $this->obj->expires_in = 123; + $this->obj->refresh_token = 'baz'; + $this->obj->refresh_token_expires_in = 321; + $this->obj->error = 'foo'; + $this->obj->error_description = 'bar'; + $this->obj->error_uri = 'baz'; + + $this->assertEquals('foo', $this->obj->access_token); + $this->assertEquals('bar', $this->obj->token_type); + $this->assertEquals(123, $this->obj->expires_in); + $this->assertEquals('baz', $this->obj->refresh_token); + $this->assertEquals(321, $this->obj->refresh_token_expires_in); + $this->assertEquals('foo', $this->obj->error); + $this->assertEquals('bar', $this->obj->error_description); + $this->assertEquals('baz', $this->obj->error_uri); + } +} diff --git a/test/OAuth/Types/RefreshUserTokenRestRequestTest.php b/test/OAuth/Types/RefreshUserTokenRestRequestTest.php new file mode 100644 index 000000000..cf7c4d1db --- /dev/null +++ b/test/OAuth/Types/RefreshUserTokenRestRequestTest.php @@ -0,0 +1,35 @@ +obj = new RefreshUserTokenRestRequest(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestRequest', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->grant_type = 'foo'; + $this->obj->refresh_token = 'bar'; + $this->obj->scope = ['foo', 'bar', 'baz']; + + $this->assertEquals('foo', $this->obj->grant_type); + $this->assertEquals('bar', $this->obj->refresh_token); + $this->assertInstanceOf('\DTS\eBaySDK\Types\RepeatableType', $this->obj->scope); + } +} diff --git a/test/OAuth/Types/RefreshUserTokenRestResponseTest.php b/test/OAuth/Types/RefreshUserTokenRestResponseTest.php new file mode 100644 index 000000000..e73245c3f --- /dev/null +++ b/test/OAuth/Types/RefreshUserTokenRestResponseTest.php @@ -0,0 +1,43 @@ +obj = new RefreshUserTokenRestResponse(); + } + + public function testCanBeCreated() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Types\RefreshUserTokenRestResponse', $this->obj); + } + + public function testExtendsBaseType() + { + $this->assertInstanceOf('\DTS\eBaySDK\Types\BaseType', $this->obj); + } + + public function testProperties() + { + $this->obj->access_token = 'foo'; + $this->obj->token_type = 'bar'; + $this->obj->expires_in = 123; + $this->obj->refresh_token = 'baz'; + $this->obj->error = 'foo'; + $this->obj->error_description = 'bar'; + $this->obj->error_uri = 'baz'; + + $this->assertEquals('foo', $this->obj->access_token); + $this->assertEquals('bar', $this->obj->token_type); + $this->assertEquals(123, $this->obj->expires_in); + $this->assertEquals('baz', $this->obj->refresh_token); + $this->assertEquals('foo', $this->obj->error); + $this->assertEquals('bar', $this->obj->error_description); + $this->assertEquals('baz', $this->obj->error_uri); + } +} diff --git a/test/SDKTest.php b/test/SDKTest.php index ae6a27cba..d81cfef1a 100644 --- a/test/SDKTest.php +++ b/test/SDKTest.php @@ -16,6 +16,7 @@ protected function setUp() 'authorization' => '', 'credentials' => ['appId' => '', 'certId' => '', 'devId' => ''], 'globalId' => '', + 'ruName' => '', 'siteId' => 0 ]); } @@ -64,6 +65,11 @@ public function testCallingUnknownMethod() $this->sdk->foo(); } + public function testCanCreateOAuth() + { + $this->assertInstanceOf('\DTS\eBaySDK\OAuth\Services\OAuthService', $this->sdk->createOAuth()); + } + public function testCanCreateAccount() { $this->assertInstanceOf('\DTS\eBaySDK\Account\Services\AccountService', $this->sdk->createAccount());