add connection

This commit is contained in:
Daniel Seifert 2024-12-20 23:45:55 +01:00
bovenliggende 33963563ed
commit 7a157033f3
Getekend door: DanielS
GPG sleutel-ID: 6A513E13AEE66170
9 gewijzigde bestanden met toevoegingen van 463 en 0 verwijderingen

21
LICENSE Normal file
Bestand weergeven

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 ProductFlow B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

45
README.md Normal file
Bestand weergeven

@ -0,0 +1,45 @@
![stability-wip](https://img.shields.io/badge/stability-work_in_progress-lightgrey.svg) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/d3datadevelopment/klicktipp-php-client?include_prereleases) [![MIT License](https://img.shields.io/github/license/d3datadevelopment/klicktipp-php-client)](https://git.d3data.de/D3Private/klicktipp-php-client/raw/branch/main/LICENSE)
# klicktipp-php-client
An unofficial client for the Klicktipp API.
## Installation
This project can easily be installed through Composer.
```
composer require d3/klicktipp-php-client
```
## Set-up connection
Prepare the client for connecting to Klicktipp with your client key and secret key.
```php
$klicktipp = new \D3\KlicktippPhpClient\Klicktipp(
$clientkey,
$secretkey,
new \GuzzleHttp\Client(...) // optional
);
```
## search a subscriber
```php
$subscriberId = $klicktipp->subscriber()->search('me@johndoe.net');
$subscriber = $klicktipp->subscriber()->search($subscriberId);
```
## Supported endpoints (still being added)
[API](https://www.klicktipp.com/de/support/wissensdatenbank/rest-application-programming-interface-api/)
:white_check_mark: = Done, and tested<br />
:ballot_box_with_check: = Done, but not yet tested<br />
:x: = Not yet developed<br />
:heavy_exclamation_mark: = deprecated/not supported <br />
| Endpoint | Status |
|--------------------------------------------------------------------------------------|-------------------------|
| account | :ballot_box_with_check: |
| subscriber | :ballot_box_with_check: |
| tag | :white_check_mark: |

Bestand weergeven

@ -17,6 +17,8 @@
],
"require": {
"php": ">=8.0",
"composer/composer": "^2.7.1",
"doctrine/collections": "^1.8.0",
"guzzlehttp/guzzle": "~7.0",
"ext-json": "*"
},

173
src/Connection.php Normal file
Bestand weergeven

@ -0,0 +1,173 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\KlicktippPhpClient;
use Composer\InstalledVersions;
use D3\KlicktippPhpClient\Exceptions\BaseException;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
use RuntimeException;
class Connection
{
public const URL = 'https://api.klicktipp.com/';
public const USERAGENT = 'Klicktipp-php-client';
protected string $client_key;
protected string $secret_key;
protected ?string $cookie = null;
/**
* Contains the HTTP client (e.g. Guzzle)
*/
private ClientInterface $client;
public function __construct(string $client_key, string $secret_key)
{
$this->client_key = $client_key;
$this->secret_key = $secret_key;
}
public function getClientKey(): string
{
return $this->client_key;
}
public function getSecretKey(): string
{
return $this->secret_key;
}
/**
* @param ClientInterface $client
*/
public function setClient(ClientInterface $client): void
{
$this->client = $client;
}
/**
* @return ClientInterface
*/
public function getClient(): ClientInterface
{
$this->client = $this->client ??
new Client([
'base_uri' => self::URL,
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'User-Agent' => self::USERAGENT.'/'.InstalledVersions::getVersion('d3/klicktipp-php-client')
]
]);
return $this->client;
}
/**
* @param string $method
* @param string $uri
* @param array $options
* @return ResponseInterface
* @throws BaseException|GuzzleException
*/
public function request(string $method, string $uri, array $options = []): ResponseInterface
{
try {
$options['query'] = $options['query'] ?? [];
if (! empty($options['body'])) {
$options['body'] = json_encode($options['body']);
}
$header = [
'Cookie' => $this->cookie ?? ''
];
$options['headers'] = $header;
return $this->getClient()->request($method, $uri, $options);
} catch (RequestException $e) {
if ($e->hasResponse()) {
$this->parseResponse($e->getResponse());
}
throw new BaseException(
$e->getResponse()->getBody(),
$e->getResponse()->getStatusCode(),
$e
);
}
}
/**
* @param string $method
* @param string $uri
* @param array $options
* @return array
* @throws BaseException
* @throws GuzzleException
*/
public function requestAndParse(string $method, string $uri, array $options = []): array
{
return $this->parseResponse($this->request($method, $uri, $options));
}
/**
* @param ResponseInterface $response
* @return array Parsed JSON result
* @throws BaseException
*/
public function parseResponse(ResponseInterface $response): array
{
try {
// Rewind the response (middlewares might have read it already)
$response->getBody()->rewind();
$response_body = $response->getBody()->getContents();
$result_array = json_decode($response_body, true);
if ($response->getStatusCode() === 204) {
return [];
}
if (! is_array($result_array)) {
throw new BaseException(
sprintf('%s: %s', $response->getStatusCode(), $response_body),
$response->getStatusCode()
);
}
return $result_array;
} catch (RuntimeException $e) {
throw new BaseException(
$e->getMessage(),
0,
$e
);
}
}
public function setCookie(?string $cookie): void
{
$this->cookie = $cookie;
}
}

Bestand weergeven

@ -0,0 +1,14 @@
<?php
namespace D3\KlicktippPhpClient\Exceptions;
use Exception;
class BaseException extends Exception
{
public function __construct($message = "", $code = 0, Exception $previous = null)
{
$message = 'Klicktipp error: '.$message;
parent::__construct($message, $code, $previous);
}
}

Bestand weergeven

@ -0,0 +1,7 @@
<?php
namespace D3\KlicktippPhpClient\Exceptions;
class NoCredentialsException extends BaseException
{
}

74
src/Klicktipp.php Normal file
Bestand weergeven

@ -0,0 +1,74 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\KlicktippPhpClient;
use D3\KlicktippPhpClient\Exceptions\BaseException;
use D3\KlicktippPhpClient\Resources\Account;
use D3\KlicktippPhpClient\Resources\Subscriber;
use D3\KlicktippPhpClient\Resources\Tag;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
class Klicktipp
{
protected string $client_key;
protected string $secret_key;
protected ?Connection $connection = null;
/**
* @throws BaseException
* @throws GuzzleException
*/
public function __construct(
string $client_key,
string $secret_key,
ClientInterface $client = null
){
$this->client_key = $client_key;
$this->secret_key = $secret_key;
if ($client) {
$this->getConnection()->setClient($client);
}
$this->account()->login();
}
private function getConnection(): Connection
{
if (!$this->connection) {
$this->connection = new Connection($this->client_key, $this->secret_key);
}
return $this->connection;
}
public function account(): Account
{
return new Account($this->getConnection());
}
public function subscriber(): Subscriber
{
return new Subscriber($this->getConnection());
}
public function tag(): Tag
{
return new Tag($this->getConnection());
}
}

51
src/Resources/Account.php Normal file
Bestand weergeven

@ -0,0 +1,51 @@
<?php
namespace D3\KlicktippPhpClient\Resources;
use D3\KlicktippPhpClient\Exceptions\BaseException;
use GuzzleHttp\Exception\GuzzleException;
class Account extends Model
{
/**
* @throws BaseException|GuzzleException
*/
public function login(): array
{
$response = $this->connection->request(
'POST',
'account/login',
[
'query' => $this->getQuery(),
'form_params' => [
'username' => $this->connection->getClientKey(),
'password' => $this->connection->getSecretKey()
]
]
);
$this->connection->setCookie(
current($response->getHeader('set-cookie'))
);
return $this->connection->parseResponse($response);
}
/**
* @throws BaseException|GuzzleException
*/
public function logout(): bool
{
$response = $this->connection->requestAndParse(
'POST',
'account/logout',
['query' => $this->getQuery()]
);
if (current($response)) {
$this->connection->setCookie(null);
}
return (bool) current($response);
}
}

76
src/Resources/Model.php Normal file
Bestand weergeven

@ -0,0 +1,76 @@
<?php
namespace D3\KlicktippPhpClient\Resources;
use D3\KlicktippPhpClient\Connection;
abstract class Model
{
protected Connection $connection;
protected array $filters = [];
protected ?int $limit = null;
protected ?int $offset = null;
protected array $query = [];
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function getLimit(): ?int
{
return $this->limit;
}
public function setLimit(int $limit): static
{
$this->limit = $limit;
return $this;
}
public function getOffset(): ?int
{
return $this->offset;
}
public function setOffset(int $offset): static
{
$this->offset = $offset;
return $this;
}
public function setFilter(string $column, $operation, $value = null): static
{
if (is_null($value)) {
$value = $operation;
$operation = 'eq';
}
$this->filters[$column][$operation] = $value;
return $this;
}
public function getQuery(): array
{
return array_filter(array_merge(
$this->query,
$this->filters,
[ 'limit' => $this->getLimit(), 'offset' => $this->getOffset() ]
));
}
public function setQuery(string|array $key, mixed $value = null): static
{
if (is_array($key)) {
$this->query = $key;
} else {
$this->query[$key] = $value;
}
return $this;
}
}