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

View File

@ -26,6 +26,10 @@ class ChillDocGeneratorExtension extends Extension implements PrependExtensionIn
{
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->load('services.yaml');
$loader->load('services/controller.yaml');
@ -37,6 +41,26 @@ class ChillDocGeneratorExtension extends Extension implements PrependExtensionIn
{
$this->preprendRoutes($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)

View File

@ -14,21 +14,36 @@ namespace Chill\DocGeneratorBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
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
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_calendar');
$rootNode = $treeBuilder->getRootNode('chill_calendar');
$treeBuilder = new TreeBuilder('chill_doc_generator');
$rootNode = $treeBuilder->getRootNode();
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
$rootNode
->children()
->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;
}

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
autoconfigure: true
Chill\DocGeneratorBundle\GeneratorDriver\:
resource: "../GeneratorDriver/"
autowire: true
autoconfigure: true
Chill\DocGeneratorBundle\Driver\RelatorioDriver: '@Chill\DocGeneratorBundle\Driver\DriverInterface'
Chill\DocGeneratorBundle\Context\ContextManager:
arguments:
$contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey }

View File

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