Skip to content

Commit

Permalink
Merge pull request #48 from quantcdn/feature/quant-tome
Browse files Browse the repository at this point in the history
Add the quant tome module.
  • Loading branch information
steveworley authored Sep 23, 2022
2 parents 9edbc37 + c63da40 commit d81d0a6
Show file tree
Hide file tree
Showing 7 changed files with 428 additions and 14 deletions.
6 changes: 6 additions & 0 deletions modules/quant_tome/drush.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
quant_tome.commands:
class: \Drupal\quant_tome\Commands\QuantTomeCommands
arguments: ['@quant_tome.deploy_batch']
tags:
- { name: drush.command }
9 changes: 9 additions & 0 deletions modules/quant_tome/quant_tome.info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: 'Quant Tome'
type: module
description: 'Deploy Tome static output to Quant.'
core: 8.x
core_version_requirement: ^8 || ^9
package: Quant
dependencies:
- tome:tome_static
- quant:quant_api
13 changes: 13 additions & 0 deletions modules/quant_tome/quant_tome.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
quant_tome.redirect_subscriber:
class: Drupal\quant_tome\EventSubscriber\RedirectSubscriber
arguments: ['@tome_static.generator', '@file_system']
tags:
- { name: event_subscriber }
quant_tome.deploy_batch:
class: Drupal\quant_tome\QuantTomeBatch
arguments:
- '@tome_static.generator'
- '@file_system'
- '@quant_api.client'
- '@queue'
63 changes: 63 additions & 0 deletions modules/quant_tome/src/Commands/QuantTomeCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Drupal\quant_tome\Commands;

use Drush\Commands\DrushCommands;
use Drupal\quant\Commands\QuantDrushCommands;
use Drupal\quant_tome\QuantTomeBatch;

/**
* Contains the quant:tome:deploy command.
*/
class QuantTomeCommands extends DrushCommands {

/**
* The batch builder.
*
* @var \Drupal\quant_tome\QuantTomeBatch
*/
protected $batch;

/**
* QuantTomeCommands constructor.
*
* @param \Drupal\quant_tome\QuantTomeBatch $batch
* The batch service.
*/
public function __construct(QuantTomeBatch $batch) {
$this->batch = $batch;
}

/**
* Deploy a Tome static build to Quant.
*
* @command quant:tome:deploy
*/
public function deploy(array $options = ['threads' => 5]) {
$this->io()->writeln('Preparing Tome output for Quant...');

if (!$this->batch->checkConfig()) {
$this->io()->error('Cannot connect to the Quant API. Please check the Quant configuration.');
return 1;
}
if (!$this->batch->checkBuild()) {
$this->io()->error('No Tome static build is available. Please run "drush tome:static".');
return 1;
}

$batch_builder = $this->batch->getBatch();
batch_set($batch_builder->toArray());

$result = drush_backend_batch_process();

if (!empty($result['object'][0]['errors'])) {
$this->io()->error('Deploy failed. Please consult the error log for more information.');
return 1;
}

// Process the queue after the batch has collected it.
$quant_drush = new QuantDrushCommands();
$quant_drush->message(['threads' => $options['threads']]);
}

}
68 changes: 68 additions & 0 deletions modules/quant_tome/src/EventSubscriber/RedirectSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Drupal\quant_tome\EventSubscriber;

use Drupal\Core\File\FileSystemInterface;
use Drupal\tome_static\StaticGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
* Writes to the _redirects file when redirects are generated.
*/
class RedirectSubscriber implements EventSubscriberInterface {

/**
* The static generator.
*
* @var \Drupal\tome_static\StaticGeneratorInterface
*/
protected $staticGenerator;

/**
* The file system.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;

/**
* Constructs a RedirectSubscriber object.
*
* @param \Drupal\tome_static\StaticGeneratorInterface $static_generator
* The static generator.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system.
*/
public function __construct(StaticGeneratorInterface $static_generator, FileSystemInterface $file_system) {
$this->staticGenerator = $static_generator;
$this->fileSystem = $file_system;
}

/**
* Reacts to a response event.
*
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
* The event.
*/
public function onResponse(FilterResponseEvent $event) {
$response = $event->getResponse();
$request = $event->getRequest();
if ($request->attributes->has(StaticGeneratorInterface::REQUEST_KEY) && $response instanceof RedirectResponse) {
$base_dir = $this->staticGenerator->getStaticDirectory();
$this->fileSystem->prepareDirectory($base_dir, FileSystemInterface::CREATE_DIRECTORY);
file_put_contents("$base_dir/_redirects", $request->getPathInfo() . ' ' . $response->getTargetUrl() . "\n", FILE_APPEND);
}
}

/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = ['onResponse'];
return $events;
}

}
230 changes: 230 additions & 0 deletions modules/quant_tome/src/QuantTomeBatch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
<?php

namespace Drupal\quant_tome;

use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\quant\Plugin\QueueItem\RedirectItem;
use Drupal\quant\Plugin\QueueItem\RouteItem;
use Drupal\quant_api\Client\QuantClient;
use Drupal\tome_base\PathTrait;
use Drupal\tome_static\StaticGeneratorInterface;

/**
* Batch process for Tome static content.
*/
class QuantTomeBatch {

use PathTrait;
use DependencySerializationTrait;

/**
* The static generator.
*
* @var \Drupal\tome_static\StaticGeneratorInterface
*/
protected $static;

/**
* The file system.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;

/**
* The Quant API client.
*
* @var \Drupal\quant_api\Client\QuantClient
*/
protected $client;

/**
* The queue factory.
*
* @var \Drupal\Core\Queue\QueueFactory
*/
protected $queueFactory;

/**
* Constructor.
*
* @param \Drupal\tome_static\StaticGeneratorInterface $static
* The Tome static generator.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system interface.
* @param \Drupal\quant_api\Client\QuantClient $client
* The Quant API client.
* @param \Drupal\Core\Queue\QueueFactory $queue_factory
* The queue factory.
*/
public function __construct(StaticGeneratorInterface $static, FileSystemInterface $file_system, QuantClient $client, QueueFactory $queue_factory) {
$this->static = $static;
$this->fileSystem = $file_system;
$this->client = $client;
$this->queueFactory = $queue_factory;
}

/**
* Check to see if Quant is configured correctly.
*
* @return bool
* If we can connect to the Quant API.
*/
public function checkConfig() {
return $this->client->ping();
}

/**
* Determine if Tome export exists.
*
* @return bool
* State of the export location.
*/
public function checkBuild() {
return file_exists($this->static->getStaticDirectory());
}

/**
* Generate the batch to seed Tome exports.
*
* @return \Drupal\Core\Batch\BatchBuilder
* A batch builder object.
*/
public function getBatch() {
$batch_builder = new BatchBuilder();
$files = [];

foreach ($this->fileSystem->scanDirectory($this->static->getStaticDirectory(), '/.*/') as $file) {
$files[] = $file->uri;
}

foreach (array_chunk($files, 10) as $chunk) {
$batch_builder->addOperation([$this, 'getHashes'], [$chunk]);
}

$batch_builder->addOperation([$this, 'checkRequiredFiles']);
return $batch_builder;
}

/**
* Generate hashes of the files.
*
* Generate hashes as Quant's API would for the file content. This will reduce
* the number of files that we need to seed in the final batch operation.
*
* @todo Quant meta look up or local?
*
* @param array $files
* List of file URIs.
* @param array|\ArrayAccess &$context
* The batch context.
*/
public function getHashes(array $files, &$context) {
$file_hashes = [];
foreach ($files as $file) {
$file_hashes[$file] = md5(file_get_contents($file));
}

$context['results']['files'] = isset($context['results']['files']) ? $context['results']['files'] : [];
$context['results']['files'] = array_merge($context['results']['files'], $file_hashes);
}

/**
* Processes the hashed records and generates the deploy batch.
*
* Takes the computed file hashes and evaluates which files need to be sent
* to Quant. Then, it creates another batch operation to seed the data.
*
* @param array|\ArrayAccess $context
* The batch context.
*/
public function checkRequiredFiles(&$context) {
$file_hashes = $context['results']['files'];

$queue = $this->queueFactory->get('quant_seed_worker');
$queue->deleteQueue();

foreach ($file_hashes as $file_path => $hash) {
if (strpos($file_path, 'redirect') > -1) {
if ($handle = fopen($file_path, 'r')) {
while (!feof($handle)) {
$line = fgets($handle);
$redirect = explode(' ', $line);
$source = trim($redirect[0]);
if (empty($source)) {
break;
}
// Only use the destination URI.
$destination = parse_url(trim($redirect[1]), PHP_URL_PATH);
$queue->createItem(new RedirectItem([
'source' => $source,
'destination' => $destination,
'status_code' => 301,
]));
}
}
fclose($handle);
continue;
}

$uri = $this->pathToUri($file_path);
$item = new RouteItem([
'route' => $uri,
'uri' => $uri,
'file_path' => $file_path,
]);

$queue->createItem($item);
}
}

/**
* Convert the path to a URI.
*
* @param string $file_path
* The file path.
*
* @return string
* URI based on the file path.
*/
public function pathToUri($file_path) {
// Strip directory and index.html to match regular Quant processing.
$uri = str_replace($this->static->getStaticDirectory(), '', $file_path);
$uri = str_replace('/index.html', '', $uri);
return $uri;
}

/**
* Deploy a file to Quant.
*
* @var \Drupal\quant\Plugin\QueueItem $item
* The file item to send to Quant API.
*/
public function deploy($item, array &$context) {
\Drupal::logger('quant_tome')->notice('Sending %s', [
'%s' => $item->log(),
]);
$item->send();
}

/**
* Finish deploy process.
*
* @param bool $success
* TRUE if batch successfully completed.
* @param array $context
* Batch context.
*/
public function finish($success, array &$context) {
if ($success) {
\Drupal::logger('quant_tome')->info('Complete!');
}
else {
\Drupal::logger('quant_tome')->error('Failed to deploy all files, check the logs!');
}
}

}
Loading

0 comments on commit d81d0a6

Please sign in to comment.