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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ acseo_typesense:
typesense:
url: '%env(resolve:TYPESENSE_URL)%'
key: '%env(resolve:TYPESENSE_KEY)%'
collection_prefix: 'test_' # Optional : add prefix to all collection
# names in Typesense
# Collection settings
collections:
books: # Typesense collection name
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
},
"require-dev": {
"symfony/phpunit-bridge": "^5.0|^6.0",
"phpunit/phpunit": "^9.5"
"phpunit/phpunit": "^9.5",
"symfony/yaml": "^3.4 || ^4.4 || ^5.4 || ^6.0"
},
"autoload": {
"psr-4": { "ACSEO\\TypesenseBundle\\": "src/" }
Expand All @@ -34,7 +35,7 @@
"scripts": {
"typesenseServer": [
"Composer\\Config::disableProcessTimeout",
"docker run -i -p 8108:8108 -v/tmp/typesense-server-data-1c/:/data typesense/typesense:0.22.0 --data-dir /data --api-key=123 --listen-port 8108 --enable-cors"
"docker run -i -p 8108:8108 -v/tmp/typesense-server-data-1c/:/data typesense/typesense:0.23.0 --data-dir /data --api-key=123 --listen-port 8108 --enable-cors"
]
}
}
8 changes: 5 additions & 3 deletions src/Command/CreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$defs = $this->collectionManager->getCollectionDefinitions();

foreach ($defs as $name => $def) {
$name = $def['name'];
$typesenseName = $def['typesense_name'];
try {
$output->writeln(sprintf('<info>Deleting</info> <comment>%s</comment>', $name));
$output->writeln(sprintf('<info>Deleting</info> <comment>%s</comment> (<comment>%s</comment> in Typesense)', $name, $typesenseName));
$this->collectionManager->deleteCollection($name);
} catch (\Typesense\Exceptions\ObjectNotFound $exception) {
$output->writeln(sprintf('<comment>%s</comment> <info>does not exists</info> ', $name));
$output->writeln(sprintf('Collection <comment>%s</comment> <info>does not exists</info> ', $typesenseName));
}

$output->writeln(sprintf('<info>Creating</info> <comment>%s</comment>', $name));
$output->writeln(sprintf('<info>Creating</info> <comment>%s</comment> (<comment>%s</comment> in Typesense)', $name, $typesenseName));
$this->collectionManager->createCollection($name);
}

Expand Down
19 changes: 15 additions & 4 deletions src/DependencyInjection/ACSEOTypesenseExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ class ACSEOTypesenseExtension extends Extension
*/
private $findersConfig = [];

/**
* An array of parameters to use as configured by the extension.
*
* @var array
*/
private $parameters = [];

public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
Expand Down Expand Up @@ -70,6 +77,8 @@ private function loadClient($config, ContainerBuilder $container)
$clientDef->replaceArgument(0, $config['url']);
$clientDef->replaceArgument(1, $config['key']);
$container->setDefinition($clientId, $clientDef);

$this->parameters['collection_prefix'] = $config['collection_prefix'] ?? '';
}

/**
Expand All @@ -83,7 +92,7 @@ private function loadClient($config, ContainerBuilder $container)
private function loadCollections(array $collections, ContainerBuilder $container)
{
foreach ($collections as $name => $config) {
$collectionName = $config['collection_name'] ?? $name;
$collectionName = $this->parameters['collection_prefix'] . ($config['collection_name'] ?? $name);

$primaryKeyExists = false;

Expand Down Expand Up @@ -112,8 +121,9 @@ private function loadCollections(array $collections, ContainerBuilder $container

if (isset($config['finders'])) {
foreach ($config['finders'] as $finderName => $finderConfig) {
$finderName = $collectionName.'.'.$finderName;
$finderName = $name.'.'.$finderName;
$finderConfig['collection_name'] = $collectionName;
$finderConfig['name'] = $name;
$finderConfig['finder_name'] = $finderName;
if (!isset($finderConfig['finder_parameters']['query_by'])) {
throw new \Exception('acseo_typesense.collections.'.$finderName.'.finder_parameters.query_by must be set');
Expand Down Expand Up @@ -158,9 +168,10 @@ private function loadTransformer(ContainerBuilder $container)
private function loadCollectionsFinder(ContainerBuilder $container)
{
foreach ($this->collectionsConfig as $name => $config) {
$collectionName = $config['typesense_name'];
$collectionName = $config['name'];

$finderId = sprintf('typesense.finder.%s', $collectionName);
$finderId = sprintf('typesense.finder.%s', $name);
$finderDef = new ChildDefinition('typesense.finder');
$finderDef->replaceArgument(2, $config);

Expand All @@ -175,7 +186,7 @@ private function loadFinderServices(ContainerBuilder $container)
{
foreach ($this->findersConfig as $name => $config) {
$finderName = $config['finder_name'];
$collectionName = $config['collection_name'];
$collectionName = $config['name'];
$finderId = sprintf('typesense.finder.%s', $collectionName);

if (isset($config['finder_service'])) {
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ public function getConfigTreeBuilder(): TreeBuilder
->children()
->scalarNode('url')->isRequired()->cannotBeEmpty()->end()
->scalarNode('key')->isRequired()->cannotBeEmpty()->end()
->scalarNode('collection_prefix')->end()
->end()
->end()
->arrayNode('collections')
->info('Collection definition')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('collection_name')->end()
->scalarNode('entity')->end()
->arrayNode('fields')
->arrayPrototype()
Expand Down
3 changes: 2 additions & 1 deletion src/Manager/CollectionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public function getManagedClassNames()
{
$managedClassNames = [];
foreach ($this->collectionDefinitions as $name => $collectionDefinition) {
$managedClassNames[$name] = $collectionDefinition['entity'];
$collectionName = $collectionDefinition['typesense_name'] ?? $name;
$managedClassNames[$collectionName] = $collectionDefinition['entity'];
}

return $managedClassNames;
Expand Down
95 changes: 95 additions & 0 deletions tests/Unit/DependencyInjection/ACSEOTypesenseExtensionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace ACSEO\Bundle\TypesenseBundle\Tests\Unit\DependencyInjection;


use ACSEO\TypesenseBundle\DependencyInjection\ACSEOTypesenseExtension;
// use FOS\ElasticaBundle\Doctrine\MongoDBPagerProvider;
// use FOS\ElasticaBundle\Doctrine\ORMPagerProvider;
// use FOS\ElasticaBundle\Doctrine\PHPCRPagerProvider;
// use FOS\ElasticaBundle\Doctrine\RegisterListenersService;
// use FOS\ElasticaBundle\Persister\InPlacePagerPersister;
// use FOS\ElasticaBundle\Persister\Listener\FilterObjectsListener;
// use FOS\ElasticaBundle\Persister\PagerPersisterRegistry;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
// use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
// use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Yaml\Yaml;

/**
* @internal
*/
class ACSEOTypesenseExtensionTest extends TestCase
{

public function testTypesenseClientDefinition()
{
$containerBuilder = new ContainerBuilder();
$containerBuilder->registerExtension($extension = new ACSEOTypesenseExtension());
$containerBuilder->setParameter('kernel.debug', true);

$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__.'/fixtures'));
$loader->load('acseo_typesense.yml');

$extensionConfig = $containerBuilder->getExtensionConfig($extension->getAlias());
$extension->load($extensionConfig, $containerBuilder);

$this->assertTrue($containerBuilder->hasDefinition('typesense.client'));

$clientDefinition = $containerBuilder->findDefinition('typesense.client');

$this->assertSame('http://localhost:8108', $clientDefinition->getArgument(0));
$this->assertSame('ACSEO', $clientDefinition->getArgument(1));
}

public function testFinderServiceDefinition()
{
$containerBuilder = new ContainerBuilder();
$containerBuilder->registerExtension($extension = new ACSEOTypesenseExtension());
$containerBuilder->setParameter('kernel.debug', true);

$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__.'/fixtures'));
$loader->load('acseo_typesense.yml');

$extensionConfig = $containerBuilder->getExtensionConfig($extension->getAlias());
$extension->load($extensionConfig, $containerBuilder);

$this->assertTrue($containerBuilder->hasDefinition('typesense.finder'));
$this->assertTrue($containerBuilder->hasDefinition('typesense.finder.books'));

$finderBooksDefinition = $containerBuilder->findDefinition('typesense.finder.books');
$finderBooksDefinitionArguments = $finderBooksDefinition->getArguments();
$arguments = array_pop($finderBooksDefinitionArguments);

$this->assertSame('books', $arguments['typesense_name']);
$this->assertSame('books', $arguments['name']);
}

public function testFinderServiceDefinitionWithCollectionPrefix()
{
$containerBuilder = new ContainerBuilder();
$containerBuilder->registerExtension($extension = new ACSEOTypesenseExtension());
$containerBuilder->setParameter('kernel.debug', true);

$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__.'/fixtures'));
$loader->load('acseo_typesense_collection_prefix.yml');

$extensionConfig = $containerBuilder->getExtensionConfig($extension->getAlias());
$extension->load($extensionConfig, $containerBuilder);

$this->assertTrue($containerBuilder->hasDefinition('typesense.finder'));
$this->assertTrue($containerBuilder->hasDefinition('typesense.finder.books'));

$finderBooksDefinition = $containerBuilder->findDefinition('typesense.finder.books');
$finderBooksDefinitionArguments = $finderBooksDefinition->getArguments();
$arguments = array_pop($finderBooksDefinitionArguments);

$this->assertSame('acseo_prefix_books', $arguments['typesense_name']);
$this->assertSame('books', $arguments['name']);
}
}
48 changes: 48 additions & 0 deletions tests/Unit/DependencyInjection/fixtures/acseo_typesense.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
acseo_typesense:
typesense:
url: 'http://localhost:8108'
key: 'ACSEO'
collections:
# Collection settings
books: # Typesense collection name
entity: 'App\Entity\Book' # Doctrine Entity class
collection_name: 'books'
fields:
#
# Keeping Database and Typesense synchronized with ids
#
id: # Entity attribute name
name: id # Typesense attribute name
type: primary # Attribute type
#
# Using again id as a sortable field (int32 required)
#
sortable_id:
entity_attribute: id # Entity attribute name forced
name: sortable_id # Typesense field name
type: int32
title:
name: title
type: string
author:
name: author
type: object # Object conversion with __toString()
author.country:
name: author_country
type: string
facet: true # Declare field as facet (required to use "group_by" query option)
entity_attribute: author.country # Equivalent of $book->getAuthor()->getCountry()
genres:
name: genres
type: collection # Convert ArrayCollection to array of strings
publishedAt:
name: publishedAt
type: datetime
optional: true # Declare field as optional
default_sorting_field: sortable_id # Default sorting field. Must be int32 or float
finders:
title_or_author:
finder_parameters:
query_by : 'title,author'
limit: 10
num_typos: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
acseo_typesense:
typesense:
url: 'http://localhost:8108'
key: 'ACSEO'
collection_prefix: 'acseo_prefix_'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work with parameters / environment placeholders?

Like:

collection_prefix: "%app_name%_"
collection_prefix: "%env(APP_NAME)%_"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will !

collections:
# Collection settings
books: # Typesense collection name
entity: 'App\Entity\Book' # Doctrine Entity class
collection_name: 'books'
fields:
#
# Keeping Database and Typesense synchronized with ids
#
id: # Entity attribute name
name: id # Typesense attribute name
type: primary # Attribute type
#
# Using again id as a sortable field (int32 required)
#
sortable_id:
entity_attribute: id # Entity attribute name forced
name: sortable_id # Typesense field name
type: int32
title:
name: title
type: string
author:
name: author
type: object # Object conversion with __toString()
author.country:
name: author_country
type: string
facet: true # Declare field as facet (required to use "group_by" query option)
entity_attribute: author.country # Equivalent of $book->getAuthor()->getCountry()
genres:
name: genres
type: collection # Convert ArrayCollection to array of strings
publishedAt:
name: publishedAt
type: datetime
optional: true # Declare field as optional
default_sorting_field: sortable_id # Default sorting field. Must be int32 or float
finders:
title_or_author:
finder_parameters:
query_by : 'title,author'
limit: 10
num_typos: 2