generate document with relatorio: config and driver

This commit is contained in:
Julien Fastré 2021-12-02 17:00:09 +01:00
parent 475b40e896
commit 2245f83631
7 changed files with 177 additions and 74 deletions

View File

@ -16,6 +16,7 @@ use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
use Chill\DocGeneratorBundle\Context\ContextManager; use Chill\DocGeneratorBundle\Context\ContextManager;
use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException; use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository; use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorFactory;
@ -54,17 +55,22 @@ final class DocGeneratorTemplateController extends AbstractController
private TempUrlGeneratorInterface $tempUrlGenerator; private TempUrlGeneratorInterface $tempUrlGenerator;
private DriverInterface $driver;
public function __construct( public function __construct(
ContextManager $contextManager, ContextManager $contextManager,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository, DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
DriverInterface $driver,
LoggerInterface $logger, LoggerInterface $logger,
PaginatorFactory $paginatorFactory, PaginatorFactory $paginatorFactory,
TempUrlGeneratorInterface $tempUrlGenerator, TempUrlGeneratorInterface $tempUrlGenerator,
KernelInterface $kernel, KernelInterface $kernel,
HttpClientInterface $client HttpClientInterface $client
) { ) {
$this->contextManager = $contextManager; $this->contextManager = $contextManager;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository; $this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->driver = $driver;
$this->logger = $logger; $this->logger = $logger;
$this->paginatorFactory = $paginatorFactory; $this->paginatorFactory = $paginatorFactory;
$this->tempUrlGenerator = $tempUrlGenerator; $this->tempUrlGenerator = $tempUrlGenerator;
@ -134,71 +140,41 @@ final class DocGeneratorTemplateController extends AbstractController
throw new \Exception('Error during Decrypt ', 1); throw new \Exception('Error during Decrypt ', 1);
} }
$tmpfnameDeCrypted = tempnam($this->kernel->getCacheDir(), 'DECRYPT_DOC_TEMPLATE'); // plus ou moins if (false === $templateResource = fopen('php://memory', 'r+')) {
$this->logger->error("Could not write data to memory");
if (!$handle = fopen($tmpfnameDeCrypted, 'ab')) {
$this->logger->error("Cannot open file ({$tmpfnameDeCrypted})");
throw new HttpException(500); throw new HttpException(500);
} }
if (false === $ftemplate = fwrite($handle, $dataDecrypted)) { fwrite($templateResource, $dataDecrypted);
$this->logger->error("Cannot write to file ({$tmpfnameDeCrypted})"); rewind($templateResource);
throw new HttpException(500);
}
dump("Success, wrote (to file ({$tmpfnameDeCrypted})");
fclose($handle);
$datas = $context->getData($template, $entity, $contextGenerationData); $datas = $context->getData($template, $entity, $contextGenerationData);
dump('datas compiled', $datas);
dump('process the data', $datas); $generatedResource = $this->driver->generateFromResource($templateResource, $template->getFile()->getType(), $datas, $template->getFile()->getFilename());
/* fclose($templateResource);
$templateProcessor = new TemplateProcessor($tmpfnameDeCrypted);
foreach ($datas['setValues'] as $setValuesConf) { $genDocName = 'doc_' . sprintf('%010d', mt_rand()).'odt';
$templateProcessor->setValues($setValuesConf);
}
foreach ($datas['cloneRowAndSetValues'] as $cloneRowAndSetValues) {
$templateProcessor->cloneRowAndSetValues($cloneRowAndSetValues[0], $cloneRowAndSetValues[1]);
}
$tmpfnameGenerated = tempnam($this->kernel->getCacheDir(), 'DOC_GENERATED');
$fileContent = fopen($tmpfnameGenerated, 'rb'); // the generated file content
*/
$genDocName = 'doc_' . sprintf('%010d', mt_rand()) . '.docx';
$getUrlGen = $this->tempUrlGenerator->generate( $getUrlGen = $this->tempUrlGenerator->generate(
'PUT', 'PUT',
$genDocName $genDocName
); );
unlink($tmpfnameDeCrypted);
//unlink($tmpfnameGenerated);
$client = new Client(); $client = new Client();
try { try {
/*
$putResponse = $client->request('PUT', $getUrlGen->url, [ $putResponse = $client->request('PUT', $getUrlGen->url, [
'body' => $fileContent, 'body' => $generatedResource,
]);
*/
$putResponse = $client->request('PUT', $getUrlGen->url, [
'body' => $ftemplate,
]); ]);
if ($putResponse->getStatusCode() === 201) { if ($putResponse->getStatusCode() === 201) {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$storedObject = new StoredObject(); $storedObject = new StoredObject();
$storedObject $storedObject
// currently, only docx is supported ->setType($template->getFile()->getType())
->setType('application/vnd.openxmlformats-officedocument.wordprocessingml.document')
->setFilename($genDocName); ->setFilename($genDocName);
$em->persist($storedObject); $em->persist($storedObject);

View File

@ -26,6 +26,10 @@ class ChillDocGeneratorExtension extends Extension implements PrependExtensionIn
{ {
public function load(array $configs, ContainerBuilder $container) public function load(array $configs, ContainerBuilder $container)
{ {
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('chill_doc_generator', $config);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config')); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
$loader->load('services.yaml'); $loader->load('services.yaml');
$loader->load('services/controller.yaml'); $loader->load('services/controller.yaml');
@ -37,6 +41,26 @@ class ChillDocGeneratorExtension extends Extension implements PrependExtensionIn
{ {
$this->preprendRoutes($container); $this->preprendRoutes($container);
$this->prependCruds($container); $this->prependCruds($container);
$this->prependClientConfig($container);
}
private function prependClientConfig(ContainerBuilder $container)
{
$configs = $container->getExtensionConfig($this->getAlias());
$resolvingBag = $container->getParameterBag();
$configs = $resolvingBag->resolveValue($configs);
$config = $this->processConfiguration(new Configuration(), $configs);
$container->prependExtensionConfig('framework', [
'http_client' => [
'scoped_clients' => [
'relatorio.client' => [
'scope' => $config['driver']['relatorio']['url'],
]
]
]
]);
} }
protected function prependCruds(ContainerBuilder $container) protected function prependCruds(ContainerBuilder $container)

View File

@ -14,21 +14,36 @@ namespace Chill\DocGeneratorBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
*/
class Configuration implements ConfigurationInterface class Configuration implements ConfigurationInterface
{ {
public function getConfigTreeBuilder() public function getConfigTreeBuilder()
{ {
$treeBuilder = new TreeBuilder('chill_calendar'); $treeBuilder = new TreeBuilder('chill_doc_generator');
$rootNode = $treeBuilder->getRootNode('chill_calendar'); $rootNode = $treeBuilder->getRootNode();
// Here you should define the parameters that are allowed to $rootNode
// configure your bundle. See the documentation linked above for ->children()
// more information on that topic. ->arrayNode('driver')
->addDefaultsIfNotSet()
->children()
->enumNode('type')
->isRequired()
->values(['relatorio'])
->defaultValue('relatorio')
->end()
->arrayNode('relatorio')
->addDefaultsIfNotSet()
->children()
->scalarNode('url')
->isRequired()
->defaultValue('http://relatorio:8888/')
->end()
->end()
->end()
->end()
->end()
->end()
;
return $treeBuilder; return $treeBuilder;
} }

View File

@ -0,0 +1,12 @@
<?php
namespace Chill\DocGeneratorBundle\GeneratorDriver;
interface DriverInterface
{
/**
* @param resource $template
* @return resource
*/
public function generateFromResource($template, string $resourceType, array $data, string $templateName = null);
}

View File

@ -0,0 +1,73 @@
<?php
namespace Chill\DocGeneratorBundle\GeneratorDriver;
use Nelmio\Alice\ParameterBag;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class RelatorioDriver implements DriverInterface
{
private LoggerInterface $logger;
private HttpClientInterface $relatorioClient;
private string $url;
public function __construct(
HttpClientInterface $relatorioClient,
ParameterBagInterface $parameterBag,
LoggerInterface $logger
) {
$this->relatorioClient = $relatorioClient;
$this->logger = $logger;
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
}
public function generateFromResource($template, string $resourceType, array $data, string $templateName = null)
{
$formFields = [
'variables' => \json_encode($data),
'template' => new DataPart($template, $templateName ?? uniqid('template_'), $resourceType),
];
$form = new FormDataPart($formFields);
try {
$response = $this->relatorioClient->request('POST', $this->url, [
'headers' => $form->getPreparedHeaders()->toArray(),
'body' => $form->bodyToIterable(),
]);
return $response->toStream();
} catch (HttpExceptionInterface $e) {
$this->logger->error("relatorio: error while generating document", [
'msg' => $e->getMessage(),
'response' => $e->getResponse()->getStatusCode(),
'content' => $e->getResponse()->getContent(false),
]);
throw $e;
} catch (TransportExceptionInterface $e) {
$this->logger->error("relatorio: transport exception", [
'msg' => $e->getMessage(),
'e' => $e->getTraceAsString(),
]);
throw $e;
} catch (DecodingExceptionInterface $e) {
$this->logger->error("relatorio: could not decode response", [
'msg' => $e->getMessage(),
'e' => $e->getTraceAsString(),
]);
throw $e;
}
}
}

View File

@ -26,6 +26,13 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\DocGeneratorBundle\GeneratorDriver\:
resource: "../GeneratorDriver/"
autowire: true
autoconfigure: true
Chill\DocGeneratorBundle\Driver\RelatorioDriver: '@Chill\DocGeneratorBundle\Driver\DriverInterface'
Chill\DocGeneratorBundle\Context\ContextManager: Chill\DocGeneratorBundle\Context\ContextManager:
arguments: arguments:
$contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey } $contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey }

View File

@ -34,6 +34,9 @@ var download = (button) => {
key, url key, url
; ;
console.log('keyData', keyData);
console.log('ivData', ivData);
button.textContent = labelPreparing; button.textContent = labelPreparing;
window.fetch(urlGenerator) window.fetch(urlGenerator)
@ -47,25 +50,18 @@ var download = (button) => {
.then(data => { .then(data => {
url = data.url; url = data.url;
if (keyData.length > 0) {
return window.crypto.subtle.importKey('jwk', keyData, { name: algo, iv: iv}, false, ['decrypt']); return window.crypto.subtle.importKey('jwk', keyData, { name: algo, iv: iv}, false, ['decrypt']);
}) }
.catch(e => { return Promise.resolve(undefined);
console.error("error while importing key");
console.error(e);
button.appendChild(document.createTextNode(decryptError));
}) })
.then(nKey => { .then(nKey => {
key = nKey; key = nKey;
return window.fetch(url); return window.fetch(url);
}) })
.catch(e => {
console.error("error while fetching data");
console.error(e);
button.textContent = "";
button.appendChild(document.createTextNode(fetchError));
})
.then(r => { .then(r => {
console.log('r', r);
if (r.ok) { if (r.ok) {
return r.arrayBuffer(); return r.arrayBuffer();
} else { } else {
@ -73,15 +69,15 @@ var download = (button) => {
} }
}) })
.then(buffer => { .then(buffer => {
console.log('buffer', buffer);
if (keyData.length > 0) {
return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, buffer); return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, buffer);
}) }
.catch(e => {
console.error("error while importing key"); return Promise.resolve(buffer);
console.error(e);
button.textContent = "";
button.appendChild(document.createTextNode(decryptError));
}) })
.then(decrypted => { .then(decrypted => {
console.log('decrypted', decrypted);
var var
blob = new Blob([decrypted], { type: mimeType }), blob = new Blob([decrypted], { type: mimeType }),
url = window.URL.createObjectURL(blob) url = window.URL.createObjectURL(blob)