Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ parameters:
- src/bridge/symfony/filesystem-bundle/src/Flow/Bridge/Symfony/FilesystemBundle/Resources/config/*
- src/bridge/symfony/postgresql-bundle/src/Flow/Bridge/Symfony/PostgreSqlBundle/Resources/config/*
- src/bridge/symfony/postgresql-bundle/tests/Flow/Bridge/Symfony/PostgreSqlBundle/Tests/Integration/Command/*
- src/bridge/symfony/postgresql-bundle/tests/Flow/Bridge/Symfony/PostgreSqlBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php
- src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Resources/config/instrumentation/*
- src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php
- src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Instrumentation/Messenger/TracingMiddleware.php
Expand Down
2 changes: 1 addition & 1 deletion rector.src.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
ArrayToFirstClassCallableRector::class => [
__DIR__ . '/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/FlowTelemetryExtension.php',
__DIR__ . '/src/bridge/symfony/filesystem-bundle/src/Flow/Bridge/Symfony/FilesystemBundle/DependencyInjection/Compiler/BuildFstabsPass.php',
__DIR__ . '/src/bridge/symfony/postgresql-bundle/src/Flow/Bridge/Symfony/PostgreSqlBundle/DependencyInjection/FlowPostgreSqlExtension.php',
__DIR__ . '/src/bridge/symfony/postgresql-bundle/src/Flow/Bridge/Symfony/PostgreSqlBundle/FlowPostgreSqlBundle.php',
],
])
->withCache(__DIR__ . '/var/rector/src')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public function process(ContainerBuilder $container) : void
$availableTypes = $this->collectAvailableTypes($container);

foreach ($fstabs as $fstabName => $fstabConfig) {
$resolvedFilesystems = [];

foreach ($fstabConfig['filesystems'] as $mountName => $entry) {
if (!\array_key_exists($entry['type'], $availableTypes)) {
throw new LogicException(\sprintf(
Expand All @@ -52,6 +54,8 @@ public function process(ContainerBuilder $container) : void
\implode(', ', \array_keys($availableTypes)),
));
}

$resolvedFilesystems[$mountName] = $this->resolveServiceReferences($entry);
}

$telemetryReference = $this->buildTelemetryConfigReference($container, $fstabName, $fstabConfig['telemetry'] ?? []);
Expand All @@ -61,7 +65,7 @@ public function process(ContainerBuilder $container) : void
$definition->setArguments([
new Reference(RegisterFilesystemFactoriesPass::REGISTRY_SERVICE_ID),
$fstabName,
$fstabConfig['filesystems'],
$resolvedFilesystems,
$telemetryReference,
]);
$definition->setPublic(false);
Expand Down Expand Up @@ -143,4 +147,84 @@ private function collectAvailableTypes(ContainerBuilder $container) : array

return $types;
}

/**
* @param array<string, mixed>&array{type: string} $entry
*
* @return array<string, mixed>&array{type: string}
*/
private function resolveAwsS3References(array $entry) : array
{
if (\array_key_exists('client_service_id', $entry) && \is_string($entry['client_service_id']) && $entry['client_service_id'] !== '') {
$entry['client'] = new Reference($entry['client_service_id']);
unset($entry['client_service_id']);
}

if (\array_key_exists('client', $entry) && \is_array($entry['client'])) {
$client = $entry['client'];

if (\array_key_exists('http_client_service_id', $client) && \is_string($client['http_client_service_id']) && $client['http_client_service_id'] !== '') {
$client['http_client'] = new Reference($client['http_client_service_id']);
unset($client['http_client_service_id']);
}

if (\array_key_exists('logger_service_id', $client) && \is_string($client['logger_service_id']) && $client['logger_service_id'] !== '') {
$client['logger'] = new Reference($client['logger_service_id']);
unset($client['logger_service_id']);
}

$entry['client'] = $client;
}

return $entry;
}

/**
* @param array<string, mixed>&array{type: string} $entry
*
* @return array<string, mixed>&array{type: string}
*/
private function resolveAzureBlobReferences(array $entry) : array
{
if (\array_key_exists('client_service_id', $entry) && \is_string($entry['client_service_id']) && $entry['client_service_id'] !== '') {
$entry['client'] = new Reference($entry['client_service_id']);
unset($entry['client_service_id']);
}

if (\array_key_exists('client', $entry) && \is_array($entry['client'])) {
$client = $entry['client'];

$serviceKeyMap = [
'http_client_service' => 'http_client',
'request_factory_service' => 'request_factory',
'stream_factory_service' => 'stream_factory',
'logger_service_id' => 'logger',
];

foreach ($serviceKeyMap as $configKey => $resolvedKey) {
if (\array_key_exists($configKey, $client) && \is_string($client[$configKey]) && $client[$configKey] !== '') {
$client[$resolvedKey] = new Reference($client[$configKey]);
unset($client[$configKey]);
}
}

$entry['client'] = $client;
}

return $entry;
}

/**
* @param array<string, mixed>&array{type: string} $entry
*
* @return array<string, mixed>&array{type: string}
*/
private function resolveServiceReferences(array $entry) : array
{
return match ($entry['type']) {
'aws_s3' => $this->resolveAwsS3References($entry),
'azure_blob' => $this->resolveAzureBlobReferences($entry),
default => $entry,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@
use Flow\Bridge\Symfony\FilesystemBundle\Filesystem\FilesystemFactory;
use Flow\Filesystem\Bridge\AsyncAWS\Options;
use Flow\Filesystem\Filesystem;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final readonly class AsyncAwsS3FilesystemFactory implements FilesystemFactory
{
public function __construct(private ContainerInterface $container)
{
}

public function create(string $protocol, array $config) : Filesystem
{
$allowed = ['bucket', 'client_service_id', 'client', 'options'];
$allowed = ['bucket', 'client', 'options'];
$unknown = \array_diff(\array_keys($config), $allowed);

if ($unknown !== []) {
Expand All @@ -36,27 +33,25 @@ public function create(string $protocol, array $config) : Filesystem
}

$bucket = $config['bucket'];
$clientServiceId = $config['client_service_id'] ?? null;
$clientConfig = $config['client'] ?? null;

if (($clientServiceId === null) === ($clientConfig === null)) {
if (!\array_key_exists('client', $config) || $config['client'] === null) {
throw new InvalidArgumentException('Filesystem factory for backend "aws_s3" requires exactly one of `client_service_id` or `client`.');
}

if (\is_string($clientServiceId) && $clientServiceId !== '') {
$client = $this->container->get($clientServiceId);
$client = $config['client'];

if (!$client instanceof S3Client) {
throw new InvalidArgumentException(\sprintf('Service "%s" is not an instance of %s.', $clientServiceId, S3Client::class));
}
if ($client instanceof S3Client) {
$resolvedClient = $client;
} elseif (\is_array($client)) {
/** @var array<string, mixed> $client */
$resolvedClient = $this->buildClient($client);
} else {
/** @var array<string, mixed> $clientConfig */
$client = $this->buildClient($clientConfig ?? []);
throw new InvalidArgumentException(\sprintf('Filesystem factory for backend "aws_s3" `client` must be an array or %s instance, got %s.', S3Client::class, \get_debug_type($client)));
}

$options = $this->buildOptions($config['options'] ?? null);

return aws_s3_filesystem($bucket, $client, $options, $protocol);
return aws_s3_filesystem($bucket, $resolvedClient, $options, $protocol);
}

public function type() : string
Expand All @@ -69,7 +64,7 @@ public function type() : string
*/
private function buildClient(array $clientConfig) : S3Client
{
$allowed = ['region', 'access_key_id', 'access_key_secret', 'session_token', 'endpoint', 'path_style_endpoint', 'shared_credentials_file', 'shared_config_file', 'profile', 'debug', 'http_client_service_id', 'logger_service_id'];
$allowed = ['region', 'access_key_id', 'access_key_secret', 'session_token', 'endpoint', 'path_style_endpoint', 'shared_credentials_file', 'shared_config_file', 'profile', 'debug', 'http_client', 'logger'];
$unknown = \array_diff(\array_keys($clientConfig), $allowed);

if ($unknown !== []) {
Expand All @@ -83,22 +78,20 @@ private function buildClient(array $clientConfig) : S3Client
$httpClient = null;
$logger = null;

if (\array_key_exists('http_client_service_id', $clientConfig)) {
$id = $clientConfig['http_client_service_id'];

if (\is_string($id) && $id !== '') {
$httpClient = $this->container->get($id);
if (\array_key_exists('http_client', $clientConfig) && $clientConfig['http_client'] !== null) {
if (!$clientConfig['http_client'] instanceof HttpClientInterface) {
throw new InvalidArgumentException(\sprintf('Filesystem factory for backend "aws_s3" `client.http_client_service_id` must reference a service implementing %s.', HttpClientInterface::class));
}
unset($clientConfig['http_client_service_id']);
$httpClient = $clientConfig['http_client'];
unset($clientConfig['http_client']);
}

if (\array_key_exists('logger_service_id', $clientConfig)) {
$id = $clientConfig['logger_service_id'];

if (\is_string($id) && $id !== '') {
$logger = $this->container->get($id);
if (\array_key_exists('logger', $clientConfig) && $clientConfig['logger'] !== null) {
if (!$clientConfig['logger'] instanceof LoggerInterface) {
throw new InvalidArgumentException(\sprintf('Filesystem factory for backend "aws_s3" `client.logger_service_id` must reference a service implementing %s.', LoggerInterface::class));
}
unset($clientConfig['logger_service_id']);
$logger = $clientConfig['logger'];
unset($clientConfig['logger']);
}

$keyMap = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,15 @@
use Flow\Filesystem\Bridge\Azure\Options;
use Flow\Filesystem\Filesystem;
use Http\Discovery\{Psr17FactoryDiscovery, Psr18ClientDiscovery};
use Psr\Container\ContainerInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\{RequestFactoryInterface, StreamFactoryInterface};
use Psr\Log\LoggerInterface;

final readonly class AzureBlobFilesystemFactory implements FilesystemFactory
{
public function __construct(private ContainerInterface $container)
{
}

public function create(string $protocol, array $config) : Filesystem
{
$allowed = ['container', 'client_service_id', 'client', 'options'];
$allowed = ['container', 'client', 'options'];
$unknown = \array_diff(\array_keys($config), $allowed);

if ($unknown !== []) {
Expand All @@ -40,23 +35,21 @@ public function create(string $protocol, array $config) : Filesystem
throw new InvalidArgumentException('Filesystem factory for backend "azure_blob" requires a non-empty `container` option.');
}

$container = $config['container'];
$clientServiceId = $config['client_service_id'] ?? null;
$clientConfig = $config['client'] ?? null;
$containerName = $config['container'];

if (($clientServiceId === null) === ($clientConfig === null)) {
if (!\array_key_exists('client', $config) || $config['client'] === null) {
throw new InvalidArgumentException('Filesystem factory for backend "azure_blob" requires exactly one of `client_service_id` or `client`.');
}

if (\is_string($clientServiceId) && $clientServiceId !== '') {
$blobService = $this->container->get($clientServiceId);
$client = $config['client'];

if (!$blobService instanceof BlobServiceInterface) {
throw new InvalidArgumentException(\sprintf('Service "%s" is not an instance of %s.', $clientServiceId, BlobServiceInterface::class));
}
if ($client instanceof BlobServiceInterface) {
$blobService = $client;
} elseif (\is_array($client)) {
/** @var array<string, mixed> $client */
$blobService = $this->buildBlobService($containerName, $client);
} else {
/** @var array<string, mixed> $clientConfig */
$blobService = $this->buildBlobService($container, $clientConfig ?? []);
throw new InvalidArgumentException(\sprintf('Filesystem factory for backend "azure_blob" `client` must be an array or %s instance, got %s.', BlobServiceInterface::class, \get_debug_type($client)));
}

$options = $this->buildOptions($config['options'] ?? null);
Expand All @@ -74,7 +67,7 @@ public function type() : string
*/
private function buildBlobService(string $containerName, array $clientConfig) : BlobServiceInterface
{
$allowed = ['account_name', 'auth', 'url_factory', 'http_client_service', 'request_factory_service', 'stream_factory_service', 'logger_service_id'];
$allowed = ['account_name', 'auth', 'url_factory', 'http_client', 'request_factory', 'stream_factory', 'logger'];
$unknown = \array_diff(\array_keys($clientConfig), $allowed);

if ($unknown !== []) {
Expand Down Expand Up @@ -109,10 +102,10 @@ private function buildBlobService(string $containerName, array $clientConfig) :
$authFactory = azure_shared_key_authorization_factory($accountName, $clientConfig['auth']['shared_key']);
$configuration = azure_blob_service_config($accountName, $containerName);

$httpClient = $this->resolveService($clientConfig['http_client_service'] ?? null, ClientInterface::class) ?? Psr18ClientDiscovery::find();
$requestFactory = $this->resolveService($clientConfig['request_factory_service'] ?? null, RequestFactoryInterface::class) ?? Psr17FactoryDiscovery::findRequestFactory();
$streamFactory = $this->resolveService($clientConfig['stream_factory_service'] ?? null, StreamFactoryInterface::class) ?? Psr17FactoryDiscovery::findStreamFactory();
$logger = $this->resolveService($clientConfig['logger_service_id'] ?? null, LoggerInterface::class);
$httpClient = $this->resolveResolvedService($clientConfig['http_client'] ?? null, ClientInterface::class, 'http_client_service') ?? Psr18ClientDiscovery::find();
$requestFactory = $this->resolveResolvedService($clientConfig['request_factory'] ?? null, RequestFactoryInterface::class, 'request_factory_service') ?? Psr17FactoryDiscovery::findRequestFactory();
$streamFactory = $this->resolveResolvedService($clientConfig['stream_factory'] ?? null, StreamFactoryInterface::class, 'stream_factory_service') ?? Psr17FactoryDiscovery::findStreamFactory();
$logger = $this->resolveResolvedService($clientConfig['logger'] ?? null, LoggerInterface::class, 'logger_service_id');

$httpFactory = azure_http_factory($requestFactory, $streamFactory);

Expand Down Expand Up @@ -178,16 +171,14 @@ private function buildOptions(mixed $optionsConfig) : Options
*
* @return null|T
*/
private function resolveService(mixed $serviceId, string $expectedClass) : ?object
private function resolveResolvedService(mixed $service, string $expectedClass, string $configKey) : ?object
{
if (!\is_string($serviceId) || $serviceId === '') {
if ($service === null) {
return null;
}

$service = $this->container->get($serviceId);

if (!$service instanceof $expectedClass) {
throw new InvalidArgumentException(\sprintf('Service "%s" is not an instance of %s.', $serviceId, $expectedClass));
throw new InvalidArgumentException(\sprintf('Filesystem factory for backend "azure_blob" `client.%s` must reference a service implementing %s.', $configKey, $expectedClass));
}

return $service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@
$services
->set('.flow.filesystem.factory.aws_s3', AsyncAwsS3FilesystemFactory::class)
->private()
->args([service('service_container')])
->tag('flow.filesystem.factory', ['type' => 'aws_s3']);
}

if (\class_exists(AzureBlobFilesystem::class)) {
$services
->set('.flow.filesystem.factory.azure_blob', AzureBlobFilesystemFactory::class)
->private()
->args([service('service_container')])
->tag('flow.filesystem.factory', ['type' => 'azure_blob']);
}

Expand Down
Loading
Loading