Skip to content

Commit 84d1f94

Browse files
committed
Refactor as a single-class bundle
1 parent f3322ce commit 84d1f94

File tree

9 files changed

+148
-165
lines changed

9 files changed

+148
-165
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
/composer.lock
55
/phpunit.xml
66
/vendor/
7+
/var/
78
*.swp
89
*.swo

composer.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
},
2828
"require-dev": {
2929
"laravel/pint": "^1.18.1",
30-
"phpstan/phpstan": "^1.12.6",
31-
"rector/rector": "^0.14.8",
32-
"symfony/phpunit-bridge": "^5.4|^6.3|^7.1.4"
30+
"phpstan/phpstan": "^2.1.2",
31+
"rector/rector": "^2.1.5",
32+
"symfony/phpunit-bridge": "^5.4|^6.3|^7.1.4",
33+
"symfony/framework-bundle": "^5.4|^6.3|^7.1"
3334
},
3435
"autoload": {
3536
"psr-4": {
@@ -53,7 +54,7 @@
5354
"scripts": {
5455
"lint": "pint -v",
5556
"refactor": "rector --debug",
56-
"test:lint": "pint --test -v",
57+
"test:lint": "pint --test -v ./src ./tests",
5758
"test:types": "phpstan analyse --ansi",
5859
"test:unit": "simple-phpunit --colors=always",
5960
"test": [

rector.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44

55
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
66
use Rector\Config\RectorConfig;
7+
use Rector\Php81\Rector\Array_\FirstClassCallableRector;
78
use Rector\Set\ValueObject\LevelSetList;
89
use Rector\Set\ValueObject\SetList;
910

1011
return static function (RectorConfig $rectorConfig): void {
1112
$rectorConfig->paths([
1213
__DIR__.'/src',
13-
]);
14-
15-
$rectorConfig->skip([
16-
__DIR__.'/src/Resources/config/',
14+
__DIR__.'/tests',
15+
__DIR__.'/rector.php',
1716
]);
1817

1918
$rectorConfig->rules([
@@ -28,4 +27,8 @@
2827
SetList::TYPE_DECLARATION,
2928
SetList::PRIVATIZATION,
3029
]);
30+
31+
$rectorConfig->skip([
32+
FirstClassCallableRector::class,
33+
]);
3134
};

src/DependencyInjection/Configuration.php

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/DependencyInjection/OpenAIExtension.php

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/OpenAIBundle.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,51 @@
44

55
namespace OpenAI\Symfony;
66

7-
use Symfony\Component\HttpKernel\Bundle\Bundle;
7+
use OpenAI\Client;
8+
use OpenAI\Contracts\ClientContract;
9+
use OpenAI\Factory;
10+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
11+
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
12+
use Symfony\Component\DependencyInjection\ContainerBuilder;
13+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
14+
use Symfony\Component\HttpClient\Psr18Client;
15+
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
816

9-
final class OpenAIBundle extends Bundle {}
17+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
18+
19+
final class OpenAIBundle extends AbstractBundle
20+
{
21+
protected string $extensionAlias = 'openai';
22+
23+
public function configure(DefinitionConfigurator $definition): void
24+
{
25+
$root = $definition->rootNode();
26+
assert($root instanceof ArrayNodeDefinition);
27+
$children = $root->children();
28+
$children->scalarNode('api_key')->defaultValue('%env(OPENAI_API_KEY)%');
29+
$children->scalarNode('organization')->defaultValue('%env(default::OPENAI_ORGANIZATION)%');
30+
}
31+
32+
/**
33+
* @param array{api_key: string, organization: string} $config
34+
*/
35+
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
36+
{
37+
$container->services()
38+
->set('openai.http_client', Psr18Client::class)
39+
->arg(0, service('http_client'))
40+
41+
->set(Factory::class)
42+
->factory([\OpenAI::class, 'factory'])
43+
->call('withHttpClient', [service('openai.http_client')])
44+
->call('withHttpHeader', ['OpenAI-Beta', 'assistants=v2'])
45+
->call('withApiKey', [$config['api_key']])
46+
->call('withOrganization', [$config['organization']])
47+
48+
->set(Client::class)
49+
->factory([service(Factory::class), 'make'])
50+
51+
->alias(ClientContract::class, Client::class)
52+
->alias('openai', Client::class);
53+
}
54+
}

src/Resources/config/services.php

Lines changed: 0 additions & 28 deletions
This file was deleted.

tests/DependencyInjection/OpenAIExtensionTest.php

Lines changed: 0 additions & 52 deletions
This file was deleted.

tests/OpenAIBundleTest.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Symfony\Tests;
6+
7+
use OpenAI\Client;
8+
use OpenAI\Contracts\ClientContract;
9+
use OpenAI\Symfony\OpenAIBundle;
10+
use PHPUnit\Framework\TestCase;
11+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
12+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
13+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
14+
use Symfony\Component\HttpClient\MockHttpClient;
15+
use Symfony\Component\HttpClient\Response\MockResponse;
16+
use Symfony\Component\HttpKernel\Kernel;
17+
18+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
19+
20+
final class OpenAIBundleTest extends TestCase
21+
{
22+
public function test_service(): void
23+
{
24+
$kernel = new class('test', true) extends Kernel
25+
{
26+
use MicroKernelTrait;
27+
28+
public function registerBundles(): iterable
29+
{
30+
yield new FrameworkBundle;
31+
yield new OpenAIBundle;
32+
}
33+
34+
protected function configureContainer(ContainerConfigurator $container): void
35+
{
36+
$container->extension('framework', [
37+
'secret' => 'S0ME_SECRET',
38+
]);
39+
40+
$container->extension('openai', [
41+
'api_key' => 'pk-123456789',
42+
'organization' => 'org-123456789',
43+
]);
44+
45+
$container->services()
46+
->set('http_client', MockHttpClient::class)
47+
->public()
48+
49+
->set('tested_services', \ArrayObject::class)
50+
->args([[
51+
'openai' => service('openai'),
52+
Client::class => service(Client::class),
53+
ClientContract::class => service(ClientContract::class),
54+
]])
55+
->public();
56+
}
57+
};
58+
59+
// Using a mock to test the service configuration
60+
$httpClient = new MockHttpClient(function (string $method, string $url, array $options = []): MockResponse {
61+
self::assertSame('DELETE', $method);
62+
self::assertSame('https://api.openai.com/v1/files/file.txt', $url);
63+
self::assertContains('Authorization: Bearer pk-123456789', $options['headers']);
64+
65+
return new MockResponse('{"id":"file.txt","object":"file","deleted":true}', [
66+
'http_code' => 200,
67+
'response_headers' => [
68+
'content-type' => 'application/json',
69+
'x-request-id' => '0123456789abcdef0123456789abcdef',
70+
],
71+
]);
72+
});
73+
74+
$kernel->boot();
75+
$container = $kernel->getContainer();
76+
$container->set('http_client', $httpClient);
77+
78+
$testedServices = $container->get('tested_services');
79+
assert($testedServices instanceof \ArrayObject);
80+
$openai = $testedServices['openai'];
81+
self::assertInstanceOf(Client::class, $openai);
82+
self::assertSame($openai, $testedServices[Client::class]);
83+
self::assertSame($openai, $testedServices[ClientContract::class]);
84+
85+
$response = $openai->files()->delete('file.txt');
86+
self::assertSame('file.txt', $response->id);
87+
}
88+
}

0 commit comments

Comments
 (0)