Skip to content

Commit 8dd00ec

Browse files
committed
batman
0 parents  commit 8dd00ec

File tree

10 files changed

+359
-0
lines changed

10 files changed

+359
-0
lines changed

.continueignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/*
2+
*.lock
3+
*.cache
4+
phpunit.phar

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/*
2+
*.lock
3+
*.cache
4+
phpunit.phar

composer.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "ay4t/php-rest-client",
3+
"description": "Multipurpose PHP rest client for consuming RESTful web services.",
4+
"keywords": ["rest","client","http client"],
5+
"homepage": "https://github.com/ay4t/php-rest-client",
6+
"type": "library",
7+
"version": "1.0.0",
8+
"require": {
9+
"guzzlehttp/guzzle": "8.0.x-dev"
10+
},
11+
"license": "MIT",
12+
"autoload": {
13+
"psr-4": {
14+
"Ay4t\\RestClient\\": "src/"
15+
}
16+
},
17+
"authors": [
18+
{
19+
"name": "Ayatulloh Ahad R",
20+
"email": "kawoel@gmail.com"
21+
}
22+
],
23+
"minimum-stability": "dev",
24+
"prefer-stable": true,
25+
"require-dev": {
26+
"phpunit/phpunit": "^11.4"
27+
}
28+
}

src/Abstracts/AbstractClient.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Ay4t\RestClient\Abstracts;
4+
5+
use Ay4t\RestClient\Config\Config;
6+
use GuzzleHttp\Client as GuzzleClient;
7+
use Ay4t\RestClient\Interfaces\ClientInterface;
8+
9+
abstract class AbstractClient implements ClientInterface
10+
{
11+
protected $client;
12+
protected $config;
13+
14+
public function __construct(Config $config)
15+
{
16+
$this->config = $config;
17+
$this->client = new GuzzleClient(['base_uri' => $config->apiUrl]);
18+
}
19+
20+
protected function prepareHeaders(): array
21+
{
22+
return [
23+
'Authorization' => 'Bearer ' . $this->config->apiKey,
24+
'Accept' => 'application/json',
25+
];
26+
}
27+
}

src/Client.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Ay4t\RestClient;
4+
5+
use GuzzleHttp\Psr7\Request;
6+
use Ay4t\RestClient\Traits\RequestTrait;
7+
use GuzzleHttp\Exception\GuzzleException;
8+
use Ay4t\RestClient\Abstracts\AbstractClient;
9+
use Ay4t\RestClient\Interfaces\ClientInterface;
10+
11+
class Client extends AbstractClient implements ClientInterface
12+
{
13+
use RequestTrait;
14+
15+
/**
16+
* Perform a request to the API server.
17+
*
18+
* @param string $method The HTTP method to use for the request.
19+
* @param string $command The command to send to the API.
20+
* @param array $params The parameters to send in the request.
21+
* @return mixed The response from the API server.
22+
* @throws \Exception If the request to the API server fails.
23+
*/
24+
public function cmd(string $method = 'GET', string $command, array $params = [])
25+
{
26+
$url = $this->config->apiUrl . '/' . $command;
27+
$options = [
28+
'headers' => $this->prepareHeaders(),
29+
'query' => $method === 'GET' ? $params : [],
30+
'json' => $method === 'POST' ? $params : [],
31+
];
32+
33+
try {
34+
$response = $this->client->request($method, $url, $options);
35+
return json_decode($response->getBody()->getContents(), $this->response_associative);
36+
} catch (GuzzleException $e) {
37+
// Error handling
38+
throw new \Exception('API request failed: ' . $e->getMessage());
39+
}
40+
}
41+
}

src/Config/Config.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Ay4t\RestClient\Config;
4+
5+
class Config
6+
{
7+
/**
8+
* The API key used for authentication.
9+
* @var string
10+
*/
11+
public $apiKey;
12+
13+
14+
/**
15+
* The secret key used for authentication.
16+
* @var string
17+
*/
18+
public $secretKey;
19+
20+
/**
21+
* The base URL of the API endpoint.
22+
* @var string
23+
*/
24+
public $apiUrl;
25+
26+
private function validateParameters(string $apiKey, string $secretKey, string $apiUrl): bool
27+
{
28+
if (empty($apiKey) || empty($secretKey) || empty($apiUrl)) {
29+
throw new \InvalidArgumentException('API key, secret key, dan API URL tidak boleh kosong');
30+
}
31+
32+
if (!is_string($apiKey) || !is_string($secretKey) || !is_string($apiUrl)) {
33+
throw new \InvalidArgumentException('Semua parameter harus berupa string');
34+
}
35+
36+
if (strlen($apiKey) < 10 || strlen($secretKey) < 10) {
37+
throw new \InvalidArgumentException('API key dan secret key harus memiliki panjang minimal 10 karakter');
38+
}
39+
40+
if (!preg_match('/^https?:\/\//', $apiUrl)) {
41+
throw new \InvalidArgumentException('API URL tidak valid');
42+
}
43+
44+
return true;
45+
}
46+
47+
public function __construct(string $apiKey, string $secretKey, string $apiUrl)
48+
{
49+
if ($this->validateParameters($apiKey, $secretKey, $apiUrl)) {
50+
$this->apiKey = $apiKey;
51+
$this->secretKey = $secretKey;
52+
$this->apiUrl = $apiUrl;
53+
}
54+
}
55+
56+
}

src/Interfaces/ClientInterface.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Ay4t\RestClient\Interfaces;
4+
5+
interface ClientInterface
6+
{
7+
/**
8+
* @param string $command
9+
* @param array $params
10+
* @param string $method
11+
* @return mixed
12+
*/
13+
public function cmd(string $method = 'GET', string $command, array $params = []);
14+
}

src/Traits/RequestTrait.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Ay4t\RestClient\Traits;
4+
5+
trait RequestTrait
6+
{
7+
/**
8+
* Set response to associative array or object
9+
* @var boolean
10+
*/
11+
protected $response_associative = true;
12+
13+
14+
/**
15+
* Set response to associative array or object
16+
* @param {boolean} value if true, response will be associative array or object
17+
* @return {this} instance of class
18+
*/
19+
public function setResponseAssociative(bool $value) : self
20+
{
21+
$this->response_associative = $value;
22+
return $this;
23+
}
24+
25+
public function prepareParams(array $params): string
26+
{
27+
return http_build_query($params);
28+
}
29+
}

tests/ClientTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
require __DIR__ . '/../vendor/autoload.php';
4+
5+
use Ay4t\RestClient\Client;
6+
use GuzzleHttp\Psr7\Request;
7+
use GuzzleHttp\Psr7\Response;
8+
use GuzzleHttp\Client as GuzzleClient;
9+
use GuzzleHttp\HandlerStack;
10+
use GuzzleHttp\Handler\CurlHandler;
11+
use GuzzleHttp\Handler\MockHandler;
12+
use GuzzleHttp\Exception\RequestException;
13+
use PHPUnit\Framework\TestCase;
14+
use Ay4t\RestClient\Abstracts\AbstractClient;
15+
use Ay4t\RestClient\Config\Config;
16+
use Ay4t\RestClient\Traits\RequestTrait;
17+
18+
class ClientTest extends TestCase
19+
{
20+
private $client;
21+
private $mockHandler;
22+
private $handlerStack;
23+
24+
/* api endpoint Ollama LLM */
25+
private $base_url_test = 'http://127.0.0.1:8080';
26+
27+
/**
28+
* __construct
29+
* @return parent::__construct()
30+
*/
31+
public function init()
32+
{
33+
$this->mockHandler = new MockHandler();
34+
$this->handlerStack = HandlerStack::create($this->mockHandler);
35+
36+
$config = new Config('api_key_panjang_sampai_10_karakter', 'secret_key_panjang_sampai_10_karakter', $this->base_url_test);
37+
$this->client = new Client($config);
38+
}
39+
40+
41+
public function testCmdSuccessPostRequest()
42+
{
43+
$this->init();
44+
45+
// Test
46+
$result = $this->client->cmd('POST', 'v1/chat/completions', [
47+
'model' => 'llama3.2:latest',
48+
'messages' => [
49+
[
50+
'role' => 'user',
51+
'content' => 'hi, why is sea water salty?',
52+
], [
53+
'role' => 'system',
54+
'content' => 'You are a helpful assistant.',
55+
]
56+
],
57+
'stream' => false,
58+
]);
59+
60+
// $llm_response = $result->choices[0]->message->content;
61+
// $llm_response = $result;
62+
print_r($result);
63+
64+
$llm_response = $result['choices'][0]['message']['content'];
65+
print_r($llm_response);
66+
67+
$this->assertTrue(!empty($llm_response));
68+
69+
}
70+
71+
}

tests/ConfigTest.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
require __DIR__ . '/../vendor/autoload.php';
4+
5+
use Ay4t\RestClient\Config\Config;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class ConfigTest extends TestCase
9+
{
10+
/**
11+
* @test
12+
*/
13+
public function testKonstruktorDenganApiKeySekretKeyDanApiUrl()
14+
{
15+
$apiKey = '1234567890';
16+
$secretKey = 'abcdefghij';
17+
$apiUrl = 'https://example.com/api';
18+
19+
$config = new Config($apiKey, $secretKey, $apiUrl);
20+
21+
$this->assertEquals($apiKey, $config->apiKey);
22+
$this->assertEquals($secretKey, $config->secretKey);
23+
$this->assertEquals($apiUrl, $config->apiUrl);
24+
}
25+
26+
/**
27+
* @test
28+
*/
29+
public function testKonstruktorDenganApiKeyKosong()
30+
{
31+
$this->expectException(\InvalidArgumentException::class);
32+
33+
new Config('', 'abcdefghij', 'https://example.com/api');
34+
}
35+
36+
/**
37+
* @test
38+
*/
39+
public function testKonstruktorDenganSecretKeyKosong()
40+
{
41+
$this->expectException(\InvalidArgumentException::class);
42+
43+
new Config('1234567890', '', 'https://example.com/api');
44+
}
45+
46+
/**
47+
* @test
48+
*/
49+
public function testKonstruktorDenganApiUrlKosong()
50+
{
51+
$this->expectException(\InvalidArgumentException::class);
52+
53+
new Config('1234567890', 'abcdefghij', '');
54+
}
55+
56+
/**
57+
* @test
58+
*/
59+
public function testKonstruktorDenganApiKeyTidakValid()
60+
{
61+
$this->expectException(\InvalidArgumentException::class);
62+
63+
new Config('abc', 'abcdefghij', 'https://example.com/api');
64+
}
65+
66+
/**
67+
* @test
68+
*/
69+
public function testKonstruktorDenganSecretKeyTidakValid()
70+
{
71+
$this->expectException(\InvalidArgumentException::class);
72+
73+
new Config('1234567890', 'abc', 'https://example.com/api');
74+
}
75+
76+
/**
77+
* @test
78+
*/
79+
public function testKonstruktorDenganApiUrlTidakValid()
80+
{
81+
$this->expectException(\InvalidArgumentException::class);
82+
83+
new Config('1234567890', 'abcdefghij', 'abc');
84+
}
85+
}

0 commit comments

Comments
 (0)