Skip to content

Commit

Permalink
Merge pull request #32 from picqer/v10
Browse files Browse the repository at this point in the history
V10
  • Loading branch information
robvanaarle committed Jun 1, 2023
2 parents 37305ab + e923f02 commit 32ce665
Show file tree
Hide file tree
Showing 242 changed files with 4,032 additions and 470 deletions.
47 changes: 23 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Bol.com Retailer API client for PHP
This is an open source PHP client for the [Bol.com Retailer API](https://api.bol.com/retailer/public/Retailer-API/v8/releasenotes.html) version 8.8.
This is an open source PHP client for the [Bol.com Retailer API](https://api.bol.com/retailer/public/Retailer-API/v10/releasenotes.html) version 10.0 (BETA).

## Installation
This project can easily be installed through Composer:

```
composer require picqer/bol-retailer-php-client "^8"
composer require picqer/bol-retailer-php-client "^10"
```

## Usage
Create an instance of the client and authenticate using the [Client Credentials flow](https://api.bol.com/retailer/public/Retailer-API/authentication.html#_client_credentials_flow)
```php
$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();
$client->authenticateByClientCredentials('your-client-id', 'your-client-secret');
```

Expand All @@ -28,10 +28,10 @@ To save requests to Bol.com, you may reuse the access token:
```php
$accessToken = ... // your implementation of getting the access token from the storage

$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();
$client->setAccessToken($accessToken);

$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV8\Client $client) {
$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) {
// Called at the beginning of a request to the Retailer API when the access token was expired (or
// non-existent) and after a request that resulted in an error about an expired access token.

Expand All @@ -46,7 +46,7 @@ $client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV8\Client $cl
When authenticating using the [Code flow](https://api.bol.com/retailer/public/Retailer-API/intermediary-authorization.html), after receiving and validating the shortcode on your callback uri, you need to retrieve the first access and refresh token:

```php
$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();

$refreshToken = $client->authenticateByAuthorizationCode(
'{your-client-id}',
Expand All @@ -62,7 +62,7 @@ $orders = $client->getOrders();

The access token needs to be (re)used to make requests to the Retailer API.
```php
$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();

$accessToken = ... // your implementation of getting the access token from the storage
$client->setAccessToken($accessToken);
Expand All @@ -74,11 +74,11 @@ The access token code is valid for a limited amount of time (600 seconds at time

```php

$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();

$accessToken = ... // your implementation of getting the access token from the storage
$client->setAccessToken($accessToken);
$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV8\Client $client) {
$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) {
// Called at the beginning of a request to the Retailer API when the access token was expired or
// non-existent and after a request that resulted in an error about an expired access token.

Expand All @@ -100,12 +100,12 @@ The example above assumed your Bol.com integration account uses a refresh token
If your refresh token changes after each use ('Method 2'), then you need to store the new refresh token after refreshing. In this case a refresh token can only be used once. When multiple processes are refreshing simultaneously, there is a risk that due to race conditions a used refresh token is stored last. This means that from then on it's impossible to refresh and the user needs to manually log in again. To prevent this, you need to work with locks, in such a way that it guarantees that only the latest refresh token is stored and used. The example below uses a blocking mutex.

```php
$client = new \Picqer\BolRetailerV8\Client();
$client = new \Picqer\BolRetailerV10\Client();

$accessToken = ... // your implementation of getting the access token from the storage
$client->setAccessToken($accessToken);

$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV8\Client $client) use ($mutex) {
$client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) use ($mutex) {
// Called at the beginning of a request to the Retailer API when the access token was expired or
// non-existent and after a request that resulted in an error about an expired access token.

Expand Down Expand Up @@ -139,27 +139,26 @@ $orders = $client->getOrders();
```

## Exceptions
Methods on the Client may throw Exceptions. All Exceptions have the parent class `Picqer\BolRetailerV8\Exception\Exception`:
Methods on the Client may throw Exceptions. All Exceptions have the parent class `Picqer\BolRetailerV10\Exception\Exception`:
- `ConnectException` is thrown when a problem occurred in the connection (e.g. API server is down or a network issue). You may retry later.
- `ServerException` (extends `ConnectException`) is thrown when a problem occurred on the Server (e.g. 500 Internal Server Error). You may retry later.
- `ResponseException` is thrown when the received response could not be handled (e.g. not of proper format or unexpected type). Retrying will not help, investigation is needed.
- `UnauthorizedException` is thrown when the server responded with 400 Unauthorized (e.g. invalid credentials).
- `RateLimitException` is thrown when the throttling limit has been reached for the API user.
- `Exception` is thrown when an error occurred in the HTTP library that is not covered by the cases above. We aim to map as much as possible to either `ConnectionException` or `ResponseException`.

## Migrate to v8
If you're migrating to v8, please have a look at the official migration guides to find out what has changed:
## Migrate to v10
If you're migrating to v10, please have a look at the official migration guides to find out what has changed:
- [bol.com Retailer API migration guide from v8 to v9](https://api.bol.com/retailer/public/Retailer-API/v9/migrationguide/v8-v9/migrationguide.html)
- [bol.com Retailer API migration guide from v7 to v8](https://api.bol.com/retailer/public/Retailer-API/v8/migrationguide/v7-v8/migrationguide.html)
- [bol.com Retailer API migration guide from v6 to v7](https://api.bol.com/retailer/public/Retailer-API/v7/migrationguide/v6-v7/migrationguide.html)
- [bol.com Retailer API migration guide from v5 to v7](https://api.bol.com/retailer/public/Retailer-API/v7/migrationguide/v5-v7/migrationguide.html)

### Gradual rollout
It's easy to overlook changes when migrating to a new version, which could result in undesired behaviour. You may consider a gradual rollout to minimize impact on your business. You can achieve this by using two versions of the API client in your project and a way to test the new version with a small percentage of requests. To use different versions of this client through Composer, fork this project and use a specific version branch of that new temporary repository as dependency.

For example, if you forked it to `my-namespace/bol-retailer-php-client`, you can add v8 next to your current version with:
For example, if you forked it to `my-namespace/bol-retailer-php-client`, you can add v10 next to your current version with:

```
composer require my-namespace/bol-retailer-php-client "v8.x-dev"
composer require my-namespace/bol-retailer-php-client "v10.x-dev"
```

You might need to add that temporary repository as [vcs repository in Composer](https://getcomposer.org/doc/05-repositories.md#vcs) for this package to be visible to Composer. When the new version is running stable, remove the old version from your project and delete the fork.
Expand All @@ -172,14 +171,14 @@ Please follow the guidelines below if you want to contribute.
- Add the latest API specs of the version you want to contribute to and generate the models and client (see: 'Generated Models and Client').
- Sometimes generation fails due to an error or outputs unexpected code. Fix this in the generator class, do not alter generated classes manually.
- If a generator required a change due to a quirk in the Bol.com API specs, please add that case to the 'Known quirks' section of this README. It would be great if you check whether the current known quirks are still relevant.
- If you contribute with a new major version, any references to 'v8' have to be replaced with the new version:
- If you contribute with a new major version, any references to 'v10' have to be replaced with the new version:
- Rename the namespaces in `/src`, `/tests` and `composer.json`.
- Replace 'v8' with the new version in the test fixtures and in `BaseClient`.
- Update this README with links to the new migration guide(s) and replace 'v8' with the new version.
- Replace 'v10' with the new version in the test fixtures and in `BaseClient`.
- Update this README with links to the new migration guide(s) and replace 'v10' with the new version.
- Keep in mind that we want to support PHP 7.1 as long as possible.

## Generated Models and Client
The Client and all models are generated by the supplied [Retailer API specifications](https://api.bol.com/retailer/public/apispec/v8) (`src/OpenApi/retailer.json`) and [Shared API specification](https://api.bol.com/retailer/public/apispec/Shared%20API%20-%20v8) (`src/OpenApi/shared.json`). These specifications are merged. Generating the code ensures there are no typos, not every operation needs a test and future (minor) updates to the specifications can easily be applied. To build the classes for the latest Bol Retailer API version, replace the two specification files with the latest version first.
The Client and all models are generated by the supplied [Retailer API specifications](https://api.bol.com/retailer/public/apispec/Retailer%20API%20-%20v10) (`src/OpenApi/retailer.json`) and [Shared API specification](https://api.bol.com/retailer/public/apispec/Shared%20API%20-%20v10) (`src/OpenApi/shared.json`). These specifications are merged. Generating the code ensures there are no typos, not every operation needs a test and future (minor) updates to the specifications can easily be applied. To build the classes for the latest Bol Retailer API version, replace the two specification files with the latest version first.

The generated classes contain all data required to properly map method arguments and response data to the models: the specifications are only used to generate them.

Expand All @@ -192,7 +191,7 @@ The specifications define types for each request and response (if it needs to se

To generate the Client, the following composer script may be used:
```
# Generates Picqer\BolRetailerV8\Client
# Generates Picqer\BolRetailerV10\Client
composer run-script generate-client
```

Expand All @@ -201,7 +200,7 @@ The class names for models are equal to the keys of the array 'definitions' in t

To generate the Models, the following composer script may be used:
```
# Generates all Picqer\BolRetailerV8\Model\* models
# Generates all Picqer\BolRetailerV10\Model\* models
composer run-script generate-models
```

Expand Down
13 changes: 8 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,30 @@
},
"autoload": {
"psr-4": {
"Picqer\\BolRetailerV8\\": "src"
"Picqer\\BolRetailerV10\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Picqer\\BolRetailerV8\\Tests\\": "tests"
"Picqer\\BolRetailerV10\\Tests\\": "tests"
}
},
"scripts": {
"test": "phpunit",
"check-style": "phpcs src tests",
"fix-style": "phpcbf src tests",
"generate-client": "Picqer\\BolRetailerV8\\OpenApi\\ClientGenerator::run",
"generate-models": "Picqer\\BolRetailerV8\\OpenApi\\ModelGenerator::run"
"generate-client": "Picqer\\BolRetailerV10\\OpenApi\\ClientGenerator::run",
"generate-models": "Picqer\\BolRetailerV10\\OpenApi\\ModelGenerator::run"
},
"extra": {
"branch-alias": {
"dev-master": "8.0-dev"
}
},
"config": {
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"composer/package-versions-deprecated": false
}
}
}
2 changes: 1 addition & 1 deletion src/AuthToken.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Picqer\BolRetailerV8;
namespace Picqer\BolRetailerV10;

class AuthToken
{
Expand Down
36 changes: 25 additions & 11 deletions src/BaseClient.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<?php

namespace Picqer\BolRetailerV8;
namespace Picqer\BolRetailerV10;

use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ConnectException as GuzzleConnectException;
use Picqer\BolRetailerV8\Exception\RateLimitException;
use Picqer\BolRetailerV8\Exception\ServerException;
use Picqer\BolRetailerV8\Model\AbstractModel;
use Picqer\BolRetailerV8\Exception\ConnectException;
use Picqer\BolRetailerV8\Exception\Exception;
use Picqer\BolRetailerV8\Exception\ResponseException;
use Picqer\BolRetailerV8\Exception\UnauthorizedException;
use Picqer\BolRetailerV8\Model\Authentication\TokenResponse;
use Picqer\BolRetailerV8\Model\Authentication\TokenRequest;
use Picqer\BolRetailerV10\Exception\RateLimitException;
use Picqer\BolRetailerV10\Exception\ServerException;
use Picqer\BolRetailerV10\Model\AbstractModel;
use Picqer\BolRetailerV10\Exception\ConnectException;
use Picqer\BolRetailerV10\Exception\Exception;
use Picqer\BolRetailerV10\Exception\ResponseException;
use Picqer\BolRetailerV10\Exception\UnauthorizedException;
use Picqer\BolRetailerV10\Model\Authentication\TokenResponse;
use Picqer\BolRetailerV10\Model\Authentication\TokenRequest;
use Psr\Http\Message\ResponseInterface;

class BaseClient
{
protected const API_TOKEN_URI = 'https://login.bol.com/token';
protected const API_ENDPOINT = 'https://api.bol.com/';
protected const API_CONTENT_TYPE_JSON = 'application/vnd.retailer.v8+json';
protected const API_CONTENT_TYPE_JSON = 'application/vnd.retailer.v10+json';

/**
* @var bool Whether request will be sent to the demo endpoint.
Expand Down Expand Up @@ -407,6 +407,20 @@ private function prepareAndExecuteRequest(string $method, string $url, array $op
});
}

// pass through multipart parameters without null values
if (isset($options['multipart'])) {
$httpOptions['multipart'] = array_filter($options['multipart'], function ($value) {
return $value !== null;
});
}

// pass through form_params parameters without null values
if (isset($options['form_params'])) {
$httpOptions['form_params'] = array_filter($options['form_params'], function ($value) {
return $value !== null;
});
}

return $this->rawRequest($method, $url, $httpOptions);
}

Expand Down
Loading

0 comments on commit 32ce665

Please sign in to comment.