Skip to content

Commit

Permalink
initial release (#1)
Browse files Browse the repository at this point in the history
* php7.1 updates

* requires pbjx-bundle

* php7 cleanup

* ncr search wip

* + add tenant_id options to all commands
+ general code cleanup
+ use pbjx aware command trait for mixin loading
+ add export nodes command
+ stub reindex nodes command

* use "pipeNodes" instead of "streamNodes" from ncr.  removes confusion about "streaming".

* add "readyForNcrTraffic" method

* code cleanup cdo (wip)

* elastica config defaults

* add "ReindexNodesCommand"

* fixup "ReindexNodesCommand".  dynamodb parallel processing with no range key creates some inconsistent chunk
sizes but the reindexer is now making a copy of the array once it reaches the expected size so the resulting batch sizes
are still as expected.  removed output to eliminate confusion with ncr batching which may be different from the
batching sent for reindexing.

this can use more tlc but for a 0.1.x release it will be fine.  it works, it's just not the obvious why the chunks would
be different at the moment.

* when running ReindexNodes ensure the Node has the Indexed interface.

* add AbstractNodeType for symfony forms types which are using node mixin

* pbj form types don't yet have a handle for constructing custom identifiers.which is fine but it means we'll need to ignore the _id field for now andjust enforce that it has a value in the controller

* add documentation for key features provided by this bundle (wip)

* cleanup/organize configuration files a little better

* prep for initial release... use ncr and pbjx-bundle ~0.1
  • Loading branch information
gdbrown authored Mar 27, 2017
1 parent 48b09f4 commit 7aecd3d
Show file tree
Hide file tree
Showing 23 changed files with 1,785 additions and 11 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: php

php:
- 7.0
- 7.1

before_script:
Expand Down
162 changes: 162 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,165 @@ ncr-bundle-php

Symfony3 bundle that integrates [gdbots/ncr](https://github.com/gdbots/ncr-php) library.


# Configuration
Follow the standard [bundle install](http://symfony.com/doc/current/bundles/installation.html) using __gdbots/ncr-bundle__ as the composer package name.

> The examples below assume you're running the DynamoDb Ncr and Elastica NcrSearch.
__config.yml:__

```yaml
# many of the configurations below are defaults so you can remove them
# from your configuration, added here for reference

# these parameters would likely be in your parameters.yml file, not here
parameters:
cloud_region: 'us-west-2'
es_clusters:
default:
debug: '%kernel.debug%'
timeout: 300 # default
persistent: true # default
round_robin: true # default
servers:
- {host: '127.0.0.1', port: 9200} # default

gdbots_ncr:
ncr:
provider: dynamodb
memoizing:
enable: true # default
#
# IMPORTANT read_through notes for the memoizer
#
# If true, the NcrCache will be updated when a cache miss occurs.
# When the Pbjx request bus is in memory, then you'd want this
# to be false so there aren't two processes updating the NcrCache.
# One is the memoizer and the other are event listeners which are
# updating cache after successful get node requests.
read_through: false # default
psr6:
enabled: true # default
# by default this cache is the same cache as your application
# it is recommend to configure this separately because Ncr items
# can be in cache much longer than app cache, for example, they
# don't need to be cleared on app deployments.
provider: cache.app # default
read_through: true # default
# to customize the cache keys or tweak cache times
# you must provide your own class and override those methods.
# see Psr6Ncr::getCacheKey and Psr6Ncr::beforeSaveCacheItem
#class: Acme\Ncr\Repository\Psr6Ncr
ncr_cache:
max_items: 500 # default
dynamodb:
config:
# these apply to batch get operations
batch_size: 100 # default
pool_size: 25 # default
table_manager:
# multi-tenant applications will likely need to provide a custom
# table manager so node tables can be derived at runtime.
# class: Acme\Ncr\Repository\DynamoDb\TableManager
table_name_prefix: my-ncr # defaults to: "%kernel.environment%-ncr"
node_tables:
# any SchemaCurie not defined here will end up using the default
# the entire default key is not needed unless you're changing it
default:
class: Gdbots\Ncr\Repository\DynamoDb\NodeTable # default
table_name: multi # default
'acme:article':
table_name: article
'acme:user':
# class is optional but needed if you want to have custom
# global secondary indexes or customize items before they
# are put into the table.
class: Acme\Ncr\Repository\DynamoDb\UserTable
table_name: user
ncr_search:
provider: elastica
elastica:
# your app will at some point need to customize the queries
# override the class so you can provide these customizations.
class: Acme\Ncr\Search\Elastica\ElasticaNcrSearch
query_timeout: '500ms' # default
clusters: '%es_clusters%'
index_manager:
# multi-tenant apps will probably need to use a custom class
#class: Acme\Ncr\Search\Elastica\IndexManager
index_prefix: my-ncr # defaults to: "%kernel.environment%-ncr"
indexes:
default:
number_of_shards: 5 # default
number_of_replicas: 1 # default
types:
# types do not need to be configured unless there is a custom
# mapping or they must be put into different indexes or types
# than the automatic handling would provide
'acme:user':
index_name: members # default is "default"
type_name: member # defaults to message of qname, i.e. "user" in this example
mapper_class: Acme\Ncr\Search\Elastica\UserMapper

# typically these would be in services.yml file.
services:
# If you are using AWS ElasticSearch service, use AwsAuthV4ClientManager
gdbots_ncr.ncr_search.elastica.client_manager:
class: Gdbots\Ncr\Search\Elastica\AwsAuthV4ClientManager
arguments:
- '@aws_credentials'
- '%cloud_region%'
- '%es_clusters%'
- '@logger'
tags:
- {name: monolog.logger, channel: ncr_search}

# recommended, create your own lazy loading handler
# to optimize batch requests.
acme_ncr.ncr_lazy_loading_handler:
class: Acme\Ncr\NcrLazyLoadingHandler
arguments: ['@ncr_lazy_loader']
tags:
- {name: pbjx.event_subscriber}

```


# Controllers
It is recommended to have data retrieval be the responsibility of Pbjx requests, however, that strategy doesn't work for all uses cases. A `NcrAwareControllerTrait` is provided which gives you methods to fetch the key Ncr services from the Symfony container.


# Symfony Form Types
We have found that many node operations have a common requirement to hide/ignore certain fields that are generally only populated by the server. Extend the `Gdbots\Bundle\NcrBundle\Form\AbstractNodeType` for your `Node` types.


# Twig Extension
The `NcrExtension` provides one function called `ncr_get_node`. It is important to note that this does __NOT__ make a query to get a node, instead it pulls from `NcrCache`.

> This might change in the future, but this strategy eliminates horribly performing twig templates that make Ncr queries.
The function will accept a `MessageRef`, `NodeRef` or a string version of a NodeRef.

__Example use of ncr_get_node:__

```txt
{% set created_by = ncr_get_node(pbj.get('created_by_ref')) %}
Here is the creator:
{{ created_by }}
```


# Console Commands
This library provides the basics for creating and extracting data from the Ncr services. Run the Symfony console and look for __ncr__ commands.

```txt
ncr:create-search-storage Creates the NcrSearch storage.
ncr:create-storage Creates the Ncr storage.
ncr:describe-search-storage Describes the NcrSearch storage.
ncr:describe-storage Describes the Ncr storage.
ncr:export-nodes Pipes nodes from the Ncr to STDOUT.
ncr:reindex-nodes Pipes nodes from the Ncr and reindexes them.
```

Review the `--help` on the ncr commands for more details.
13 changes: 7 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
"type": "library",
"license": "Apache-2.0",
"require": {
"php": ">=7.0",
"gdbots/ncr": "dev-beta as 0.1.0",
"gdbots/pbjx": "dev-es as 1.0.0",
"gdbots/schemas": "dev-es as 1.0.0",
"symfony/symfony": "~3.1"
"php": ">=7.1",
"gdbots/ncr": "~0.1",
"gdbots/pbjx-bundle": "~0.1",
"symfony/symfony": "~3.2"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
"phpunit/phpunit": "~4.3"
"phpunit/phpunit": "~4.3",
"aws/aws-sdk-php": "~3.21",
"ruflin/elastica": "~3.2"
},
"autoload": {
"psr-4": {
Expand Down
99 changes: 99 additions & 0 deletions src/Command/CreateSearchStorageCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
declare(strict_types = 1);

namespace Gdbots\Bundle\NcrBundle\Command;

use Gdbots\Schemas\Ncr\Mixin\Indexed\IndexedV1Mixin;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class CreateSearchStorageCommand extends ContainerAwareCommand
{
use NcrAwareCommandTrait;

/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('ncr:create-search-storage')
->setDescription('Creates the NcrSearch storage.')
->setHelp(<<<EOF
The <info>%command.name%</info> command will create the storage for the NcrSearch.
If a SchemaQName is not provided it will run on all schemas having the mixin "gdbots:ncr:mixin:indexed".
<info>php %command.full_name% --tenant-id=client1 'acme:article'</info>
EOF
)
->addOption(
'skip-errors',
null,
InputOption::VALUE_NONE,
'Skip any schemas that fail to create.'
)
->addOption(
'context',
null,
InputOption::VALUE_REQUIRED,
'Context to provide to the NcrSearch (json).'
)
->addOption(
'tenant-id',
null,
InputOption::VALUE_REQUIRED,
'Tenant Id to use for this operation.'
)
->addArgument(
'qname',
InputArgument::OPTIONAL,
'The SchemaQName of the node. e.g. "acme:article"'
);
}

/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return null
*
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$skipErrors = $input->getOption('skip-errors');
$context = json_decode($input->getOption('context') ?: '{}', true);
$context['tenant_id'] = (string)$input->getOption('tenant-id');

$io = new SymfonyStyle($input, $output);
$io->title('NcrSearch Storage Creator');
$ncrSearch = $this->getNcrSearch();

foreach ($this->getSchemasUsingMixin(IndexedV1Mixin::create(), $input->getArgument('qname')) as $schema) {
$qname = $schema->getQName();

try {
$ncrSearch->createStorage($qname, $context);
$io->success(sprintf('Created NcrSearch storage for "%s".', $qname));
} catch (\Exception $e) {
if (!$skipErrors) {
throw $e;
}

$io->error(sprintf('Failed to create NcrSearch storage for "%s".', $qname));
$io->text($e->getMessage());
if ($e->getPrevious()) {
$io->newLine();
$io->text($e->getPrevious()->getMessage());
}

$io->newLine();
}
}
}
}
99 changes: 99 additions & 0 deletions src/Command/CreateStorageCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
declare(strict_types = 1);

namespace Gdbots\Bundle\NcrBundle\Command;

use Gdbots\Schemas\Ncr\Mixin\Node\NodeV1Mixin;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class CreateStorageCommand extends ContainerAwareCommand
{
use NcrAwareCommandTrait;

/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('ncr:create-storage')
->setDescription('Creates the Ncr storage.')
->setHelp(<<<EOF
The <info>%command.name%</info> command will create the storage for the Ncr.
If a SchemaQName is not provided it will run on all schemas having the mixin "gdbots:ncr:mixin:node".
<info>php %command.full_name% --tenant-id=client1 'acme:article'</info>
EOF
)
->addOption(
'skip-errors',
null,
InputOption::VALUE_NONE,
'Skip any schemas that fail to create.'
)
->addOption(
'context',
null,
InputOption::VALUE_REQUIRED,
'Context to provide to the NcrSearch (json).'
)
->addOption(
'tenant-id',
null,
InputOption::VALUE_REQUIRED,
'Tenant Id to use for this operation.'
)
->addArgument(
'qname',
InputArgument::OPTIONAL,
'The SchemaQName of the node. e.g. "acme:article"'
);
}

/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return null
*
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$skipErrors = $input->getOption('skip-errors');
$context = json_decode($input->getOption('context') ?: '{}', true);
$context['tenant_id'] = (string)$input->getOption('tenant-id');

$io = new SymfonyStyle($input, $output);
$io->title('Ncr Storage Creator');
$ncr = $this->getNcr();

foreach ($this->getSchemasUsingMixin(NodeV1Mixin::create(), $input->getArgument('qname')) as $schema) {
$qname = $schema->getQName();

try {
$ncr->createStorage($qname, $context);
$io->success(sprintf('Created Ncr storage for "%s".', $qname));
} catch (\Exception $e) {
if (!$skipErrors) {
throw $e;
}

$io->error(sprintf('Failed to create Ncr storage for "%s".', $qname));
$io->text($e->getMessage());
if ($e->getPrevious()) {
$io->newLine();
$io->text($e->getPrevious()->getMessage());
}

$io->newLine();
}
}
}
}
Loading

0 comments on commit 7aecd3d

Please sign in to comment.