diff --git a/pkg/gps/GpsConnectionFactory.php b/pkg/gps/GpsConnectionFactory.php index d2fcb235a..e3f61bcb2 100644 --- a/pkg/gps/GpsConnectionFactory.php +++ b/pkg/gps/GpsConnectionFactory.php @@ -4,6 +4,7 @@ namespace Enqueue\Gps; +use Enqueue\Dsn\Dsn; use Google\Cloud\PubSub\PubSubClient; use Interop\Queue\PsrConnectionFactory; use Interop\Queue\PsrContext; @@ -15,6 +16,11 @@ class GpsConnectionFactory implements PsrConnectionFactory */ private $config; + /** + * @var PubSubClient + */ + private $client; + /** * @see https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application * @see \Google\Cloud\PubSub\PubSubClient::__construct() @@ -24,6 +30,7 @@ class GpsConnectionFactory implements PsrConnectionFactory * 'keyFilePath' => The full path to your service account credentials.json file retrieved from the Google Developers Console. * 'retries' => Number of retries for a failed request. **Defaults to** `3`. * 'scopes' => Scopes to be used for the request. + * 'emulatorHost' => The endpoint used to emulate communication with GooglePubSub. * 'lazy' => 'the connection will be performed as later as possible, if the option set to true' * ] * @@ -32,17 +39,31 @@ class GpsConnectionFactory implements PsrConnectionFactory * gps: * gps:?projectId=projectName * - * @param array|string|null $config + * or instance of Google\Cloud\PubSub\PubSubClient + * + * @param array|string|PubSubClient|null $config */ public function __construct($config = 'gps:') { - if (empty($config) || 'gps:' === $config) { + if ($config instanceof PubSubClient) { + $this->client = $config; + $this->config = ['lazy' => false] + $this->defaultConfig(); + + return; + } + + if (empty($config)) { $config = []; } elseif (is_string($config)) { $config = $this->parseDsn($config); } elseif (is_array($config)) { + if (array_key_exists('dsn', $config)) { + $config = array_replace_recursive($config, $this->parseDsn($config['dsn'])); + + unset($config['dsn']); + } } else { - throw new \LogicException('The config must be either an array of options, a DSN string or null'); + throw new \LogicException(sprintf('The config must be either an array of options, a DSN string, null or instance of %s', PubSubClient::class)); } $this->config = array_replace($this->defaultConfig(), $config); @@ -64,22 +85,36 @@ public function createContext(): PsrContext private function parseDsn(string $dsn): array { - if (false === strpos($dsn, 'gps:')) { - throw new \LogicException(sprintf('The given DSN "%s" is not supported. Must start with "gps:".', $dsn)); - } - - $config = []; + $dsn = new Dsn($dsn); - if ($query = parse_url($dsn, PHP_URL_QUERY)) { - parse_str($query, $config); + if ('gps' !== $dsn->getSchemeProtocol()) { + throw new \LogicException(sprintf( + 'The given scheme protocol "%s" is not supported. It must be "gps"', + $dsn->getSchemeProtocol() + )); } - return $config; + $emulatorHost = $dsn->getQueryParameter('emulatorHost'); + $hasEmulator = $emulatorHost ? true : null; + + return array_filter(array_replace($dsn->getQuery(), [ + 'projectId' => $dsn->getQueryParameter('projectId'), + 'keyFilePath' => $dsn->getQueryParameter('keyFilePath'), + 'retries' => $dsn->getInt('retries'), + 'scopes' => $dsn->getQueryParameter('scopes'), + 'emulatorHost' => $emulatorHost, + 'hasEmulator' => $hasEmulator, + 'lazy' => $dsn->getBool('lazy'), + ]), function ($value) { return null !== $value; }); } private function establishConnection(): PubSubClient { - return new PubSubClient($this->config); + if (false == $this->client) { + $this->client = new PubSubClient($this->config); + } + + return $this->client; } private function defaultConfig(): array diff --git a/pkg/gps/Tests/GpsConnectionFactoryConfigTest.php b/pkg/gps/Tests/GpsConnectionFactoryConfigTest.php new file mode 100644 index 000000000..a1700f64f --- /dev/null +++ b/pkg/gps/Tests/GpsConnectionFactoryConfigTest.php @@ -0,0 +1,108 @@ +expectException(\LogicException::class); + $this->expectExceptionMessage('The config must be either an array of options, a DSN string, null or instance of Google\Cloud\PubSub\PubSubClient'); + + new GpsConnectionFactory(new \stdClass()); + } + + public function testThrowIfSchemeIsNotAmqp() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The given scheme protocol "http" is not supported. It must be "gps"'); + + new GpsConnectionFactory('http://example.com'); + } + + public function testThrowIfDsnCouldNotBeParsed() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The DSN is invalid.'); + + new GpsConnectionFactory('foo'); + } + + /** + * @dataProvider provideConfigs + * + * @param mixed $config + * @param mixed $expectedConfig + */ + public function testShouldParseConfigurationAsExpected($config, $expectedConfig) + { + $factory = new GpsConnectionFactory($config); + + $this->assertAttributeEquals($expectedConfig, 'config', $factory); + } + + public static function provideConfigs() + { + yield [ + null, + [ + 'lazy' => true, + ], + ]; + + yield [ + 'gps:', + [ + 'lazy' => true, + ], + ]; + + yield [ + [], + [ + 'lazy' => true, + ], + ]; + + yield [ + 'gps:?foo=fooVal&projectId=mqdev&emulatorHost=http%3A%2F%2Fgoogle-pubsub%3A8085', + [ + 'foo' => 'fooVal', + 'projectId' => 'mqdev', + 'emulatorHost' => 'http://google-pubsub:8085', + 'hasEmulator' => true, + 'lazy' => true, + ], + ]; + + yield [ + ['dsn' => 'gps:?foo=fooVal&projectId=mqdev&emulatorHost=http%3A%2F%2Fgoogle-pubsub%3A8085'], + [ + 'foo' => 'fooVal', + 'projectId' => 'mqdev', + 'emulatorHost' => 'http://google-pubsub:8085', + 'hasEmulator' => true, + 'lazy' => true, + ], + ]; + + yield [ + ['foo' => 'fooVal', 'projectId' => 'mqdev', 'emulatorHost' => 'http://Fgoogle-pubsub:8085', 'lazy' => false], + [ + 'foo' => 'fooVal', + 'projectId' => 'mqdev', + 'emulatorHost' => 'http://Fgoogle-pubsub:8085', + 'lazy' => false, + ], + ]; + } +} diff --git a/pkg/gps/composer.json b/pkg/gps/composer.json index e0563aeb6..aede85d7f 100644 --- a/pkg/gps/composer.json +++ b/pkg/gps/composer.json @@ -8,15 +8,13 @@ "require": { "php": "^7.1.3", "queue-interop/queue-interop": "0.7.x-dev", - "google/cloud-pubsub": "^0.6.1|^1.0" + "google/cloud-pubsub": "^1.0", + "enqueue/dsn": "0.9.x-dev" }, "require-dev": { "phpunit/phpunit": "~5.4.0", "enqueue/test": "0.9.x-dev", - "enqueue/enqueue": "0.9.x-dev", - "queue-interop/queue-spec": "0.6.x-dev", - "symfony/dependency-injection": "^3.4|^4", - "symfony/config": "^3.4|^4" + "queue-interop/queue-spec": "0.6.x-dev" }, "support": { "email": "opensource@forma-pro.com", @@ -31,9 +29,6 @@ "/Tests/" ] }, - "suggest": { - "enqueue/enqueue": "If you'd like to use advanced features like Client abstract layer or Symfony integration features" - }, "minimum-stability": "dev", "extra": { "branch-alias": {