mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-07-01 22:46:13 +00:00
Merge branch '393-fix-dump-only-document-generator' into 'master'
Send data dumps as email attachments instead of links, update translations,... Closes #393 See merge request Chill-Projet/chill-bundles!843
This commit is contained in:
commit
ee4e223043
6
.changes/unreleased/Fixed-20250626-122030.yaml
Normal file
6
.changes/unreleased/Fixed-20250626-122030.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixed
|
||||||
|
body: 'Doc Generation: the "dump only" method send the document as an email attachment.'
|
||||||
|
time: 2025-06-26T12:20:30.083601058+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "393"
|
||||||
|
SchemaChange: No schema change
|
@ -22,7 +22,7 @@ Chill is a comprehensive web application built as a set of Symfony bundles. It i
|
|||||||
- **Backend**: PHP 8.3+, Symfony 5.4
|
- **Backend**: PHP 8.3+, Symfony 5.4
|
||||||
- **Frontend**: JavaScript/TypeScript, Vue.js 3, Bootstrap 5
|
- **Frontend**: JavaScript/TypeScript, Vue.js 3, Bootstrap 5
|
||||||
- **Build Tools**: Webpack Encore, Yarn
|
- **Build Tools**: Webpack Encore, Yarn
|
||||||
- **Database**: PostgreSQL with materialized views
|
- **Database**: PostgreSQL with materialized views. We do not support other databases.
|
||||||
- **Other Services**: Redis, AMQP (RabbitMQ), SMTP
|
- **Other Services**: Redis, AMQP (RabbitMQ), SMTP
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
@ -149,6 +149,42 @@ Key configuration files:
|
|||||||
- `package.json`: JavaScript dependencies and scripts
|
- `package.json`: JavaScript dependencies and scripts
|
||||||
- `.env`: Default environment variables. Must usually not be updated: use `.env.local` instead.
|
- `.env`: Default environment variables. Must usually not be updated: use `.env.local` instead.
|
||||||
|
|
||||||
|
### Database migrations
|
||||||
|
|
||||||
|
Each time a doctrine entity is created, we generate migration to adapt the database.
|
||||||
|
|
||||||
|
The migration are created using the command `symfony console doctrine:migrations:diff --no-interaction --namespace <namespace>`, where the namespace is the relevant namespace for migration. As this is a bash script, do not forget to quote the `\` (`\` must become `\\` in your command).
|
||||||
|
|
||||||
|
Each bundle has his own namespace for migration (always ask me to confirm that command, with a list of updated / created entities so that I can confirm you that it is ok):
|
||||||
|
|
||||||
|
- `Chill\Bundle\ActivityBundle` writes migrations to `Chill\Migrations\Activity`;
|
||||||
|
- `Chill\Bundle\BudgetBundle` writes migrations to `Chill\Migrations\Budget`;
|
||||||
|
- `Chill\Bundle\CustomFieldsBundle` writes migrations to `Chill\Migrations\CustomFields`;
|
||||||
|
- `Chill\Bundle\DocGeneratorBundle` writes migrations to `Chill\Migrations\DocGenerator`;
|
||||||
|
- `Chill\Bundle\DocStoreBundle` writes migrations to `Chill\Migrations\DocStore`;
|
||||||
|
- `Chill\Bundle\EventBundle` writes migrations to `Chill\Migrations\Event`;
|
||||||
|
- `Chill\Bundle\CalendarBundle` writes migrations to `Chill\Migrations\Calendar`;
|
||||||
|
- `Chill\Bundle\FamilyMembersBundle` writes migrations to `Chill\Migrations\FamilyMembers`;
|
||||||
|
- `Chill\Bundle\FranceTravailApiBundle` writes migrations to `Chill\Migrations\FranceTravailApi`;
|
||||||
|
- `Chill\Bundle\JobBundle` writes migrations to `Chill\Migrations\Job`;
|
||||||
|
- `Chill\Bundle\MainBundle` writes migrations to `Chill\Migrations\Main`;
|
||||||
|
- `Chill\Bundle\PersonBundle` writes migrations to `Chill\Migrations\Person`;
|
||||||
|
- `Chill\Bundle\ReportBundle` writes migrations to `Chill\Migrations\Report`;
|
||||||
|
- `Chill\Bundle\TaskBundle` writes migrations to `Chill\Migrations\Task`;
|
||||||
|
- `Chill\Bundle\ThirdPartyBundle` writes migrations to `Chill\Migrations\ThirdParty`;
|
||||||
|
- `Chill\Bundle\TicketBundle` writes migrations to `Chill\Migrations\Ticket`;
|
||||||
|
- `Chill\Bundle\WopiBundle` writes migrations to `Chill\Migrations\Wopi`;
|
||||||
|
|
||||||
|
Once created the, comment's classes should be removed and a description of the changes made to the entities should be added to the migrations, using the `getDescription` method. The migration should not be cleaned by any artificial intelligence, as modifying this migration is error prone.
|
||||||
|
|
||||||
|
### Guidelines related to code structure and requirements
|
||||||
|
|
||||||
|
#### Usage of clock
|
||||||
|
|
||||||
|
When we need to use a DateTime or DateTimeImmutable that need to express "now", we prefer the usage of
|
||||||
|
`Symfony\Component\Clock\ClockInterface`, where possible. This is usually not possible in doctrine entities,
|
||||||
|
where injection does not work when restoring an entity from database, but usually possible in services.
|
||||||
|
|
||||||
### Testing Information
|
### Testing Information
|
||||||
|
|
||||||
The project uses PHPUnit for testing. Each bundle has its own test suite, and there's also a global test suite at the root level.
|
The project uses PHPUnit for testing. Each bundle has its own test suite, and there's also a global test suite at the root level.
|
||||||
@ -218,7 +254,7 @@ class TicketTest extends TestCase
|
|||||||
|
|
||||||
#### Test Database
|
#### Test Database
|
||||||
|
|
||||||
For tests that require a database, the project uses an in-memory SQLite database by default. You can configure a different database for testing in the `.env.test` file.
|
For tests that require a database, the project uses postgresql database filled by fixtures (usage of doctrine-fixtures). You can configure a different database for testing in the `.env.test` file.
|
||||||
|
|
||||||
### Code Quality Tools
|
### Code Quality Tools
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
{{ 'docgen.data_dump_email.Dear'|trans }}
|
{{ 'docgen.data_dump_email.Dear'|trans }}
|
||||||
|
|
||||||
{{ 'docgen.data_dump_email.data_dump_ready_and_link'|trans }}
|
{{ 'docgen.data_dump_email.data_dump_ready_and_attached'|trans }}
|
||||||
|
|
||||||
{{ link }}
|
{{ 'docgen.data_dump_email.filename'|trans({filename: filename}) }}
|
||||||
|
|
||||||
{{ 'docgen.data_dump_email.link_valid_until'|trans({validity: validity}) }}
|
|
||||||
|
@ -11,13 +11,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocGeneratorBundle\Service\Messenger;
|
namespace Chill\DocGeneratorBundle\Service\Messenger;
|
||||||
|
|
||||||
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
|
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
|
||||||
use Chill\DocGeneratorBundle\Service\Generator\Generator;
|
|
||||||
use Chill\DocGeneratorBundle\Service\Generator\GeneratorException;
|
use Chill\DocGeneratorBundle\Service\Generator\GeneratorException;
|
||||||
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
|
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
|
||||||
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
|
||||||
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@ -37,15 +37,15 @@ class RequestGenerationHandler implements MessageHandlerInterface
|
|||||||
private const LOG_PREFIX = '[docgen message handler] ';
|
private const LOG_PREFIX = '[docgen message handler] ';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
|
private readonly DocGeneratorTemplateRepositoryInterface $docGeneratorTemplateRepository,
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private readonly EntityManagerInterface $entityManager,
|
||||||
private readonly Generator $generator,
|
private readonly GeneratorInterface $generator,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private readonly StoredObjectRepository $storedObjectRepository,
|
private readonly StoredObjectRepositoryInterface $storedObjectRepository,
|
||||||
private readonly UserRepositoryInterface $userRepository,
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
private readonly MailerInterface $mailer,
|
private readonly MailerInterface $mailer,
|
||||||
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
|
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
|
private readonly StoredObjectManagerInterface $storedObjectManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function __invoke(RequestGenerationMessage $message)
|
public function __invoke(RequestGenerationMessage $message)
|
||||||
@ -90,7 +90,7 @@ class RequestGenerationHandler implements MessageHandlerInterface
|
|||||||
|
|
||||||
$this->sendDataDump($destinationStoredObject, $message);
|
$this->sendDataDump($destinationStoredObject, $message);
|
||||||
} else {
|
} else {
|
||||||
$destinationStoredObject = $this->generator->generateDocFromTemplate(
|
$this->generator->generateDocFromTemplate(
|
||||||
$template,
|
$template,
|
||||||
$message->getEntityId(),
|
$message->getEntityId(),
|
||||||
$message->getContextGenerationData(),
|
$message->getContextGenerationData(),
|
||||||
@ -122,19 +122,20 @@ class RequestGenerationHandler implements MessageHandlerInterface
|
|||||||
|
|
||||||
private function sendDataDump(StoredObject $destinationStoredObject, RequestGenerationMessage $message): void
|
private function sendDataDump(StoredObject $destinationStoredObject, RequestGenerationMessage $message): void
|
||||||
{
|
{
|
||||||
$url = $this->tempUrlGenerator->generate('GET', $destinationStoredObject->getFilename(), 3600);
|
// Get the content of the document
|
||||||
$parts = [];
|
$content = $this->storedObjectManager->read($destinationStoredObject);
|
||||||
parse_str(parse_url($url->url)['query'], $parts);
|
$filename = $destinationStoredObject->getFilename();
|
||||||
$validity = \DateTimeImmutable::createFromFormat('U', $parts['temp_url_expires']);
|
$contentType = $destinationStoredObject->getType();
|
||||||
|
|
||||||
|
// Create the email with the document as an attachment
|
||||||
$email = (new TemplatedEmail())
|
$email = (new TemplatedEmail())
|
||||||
->to($message->getSendResultToEmail())
|
->to($message->getSendResultToEmail())
|
||||||
->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
|
->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
|
||||||
->context([
|
->context([
|
||||||
'link' => $url->url,
|
'filename' => $filename,
|
||||||
'validity' => $validity,
|
|
||||||
])
|
])
|
||||||
->subject($this->translator->trans('docgen.data_dump_email.subject'));
|
->subject($this->translator->trans('docgen.data_dump_email.subject'))
|
||||||
|
->attach($content, $filename, $contentType);
|
||||||
|
|
||||||
$this->mailer->send($email);
|
$this->mailer->send($email);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Tests\Service\Messenger;
|
||||||
|
|
||||||
|
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||||
|
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
|
||||||
|
use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
|
||||||
|
use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationHandler;
|
||||||
|
use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
|
||||||
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Psr\Log\NullLogger;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RequestGenerationHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
public function testGenerationHappyScenario(): void
|
||||||
|
{
|
||||||
|
// Create entities
|
||||||
|
$template = new DocGeneratorTemplate();
|
||||||
|
$this->setPrivateProperty($template, 'id', 1);
|
||||||
|
|
||||||
|
$storedObject = new StoredObject();
|
||||||
|
$this->setPrivateProperty($storedObject, 'id', 2);
|
||||||
|
|
||||||
|
$creator = new User();
|
||||||
|
$creator->setEmail('test@example.com');
|
||||||
|
$this->setPrivateProperty($creator, 'id', 3);
|
||||||
|
|
||||||
|
$docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
|
||||||
|
$docGeneratorTemplateRepository->find(1)->willReturn($template);
|
||||||
|
|
||||||
|
$storedObjectRepository = $this->prophesize(StoredObjectRepositoryInterface::class);
|
||||||
|
$storedObjectRepository->find(2)->willReturn($storedObject);
|
||||||
|
|
||||||
|
$userRepository = $this->prophesize(UserRepositoryInterface::class);
|
||||||
|
$userRepository->find(3)->willReturn($creator);
|
||||||
|
|
||||||
|
// Create a mock for the Query object
|
||||||
|
$query = $this->prophesize(Query::class);
|
||||||
|
$query->setParameter('id', 2)->willReturn($query->reveal());
|
||||||
|
$query->execute()->shouldBeCalled();
|
||||||
|
|
||||||
|
// Create a mock for the EntityManager
|
||||||
|
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||||
|
$entityManager->createQuery(Argument::containingString('UPDATE'))->willReturn($query->reveal());
|
||||||
|
$entityManager->flush()->shouldBeCalled();
|
||||||
|
|
||||||
|
$generator = $this->prophesize(GeneratorInterface::class);
|
||||||
|
$generator->generateDocFromTemplate(
|
||||||
|
$template,
|
||||||
|
123, // entityId
|
||||||
|
['key' => 'value'], // contextGenerationData
|
||||||
|
$storedObject,
|
||||||
|
$creator
|
||||||
|
)
|
||||||
|
->willReturn($storedObject)->shouldBeCalled();
|
||||||
|
|
||||||
|
$logger = new NullLogger();
|
||||||
|
|
||||||
|
$mailer = $this->prophesize(MailerInterface::class);
|
||||||
|
|
||||||
|
$translator = $this->prophesize(TranslatorInterface::class);
|
||||||
|
|
||||||
|
$storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
|
||||||
|
|
||||||
|
// Create handler
|
||||||
|
$handler = new RequestGenerationHandler(
|
||||||
|
$docGeneratorTemplateRepository->reveal(),
|
||||||
|
$entityManager->reveal(),
|
||||||
|
$generator->reveal(),
|
||||||
|
$logger,
|
||||||
|
$storedObjectRepository->reveal(),
|
||||||
|
$userRepository->reveal(),
|
||||||
|
$mailer->reveal(),
|
||||||
|
$translator->reveal(),
|
||||||
|
$storedObjectManager->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create message
|
||||||
|
$message = new RequestGenerationMessage(
|
||||||
|
$creator,
|
||||||
|
$template,
|
||||||
|
123, // entityId
|
||||||
|
$storedObject,
|
||||||
|
['key' => 'value'], // contextGenerationData
|
||||||
|
false, // isTest
|
||||||
|
null, // sendResultToEmail
|
||||||
|
false // dumpOnly
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invoke handler
|
||||||
|
$handler->__invoke($message);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
// The assertions are handled by the shouldBeCalled() expectations on the mocks
|
||||||
|
$this->assertTrue(true); // Just to have an assertion in the test
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setPrivateProperty(object $object, string $propertyName, $value): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($object);
|
||||||
|
$property = $reflection->getProperty($propertyName);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
$property->setValue($object, $value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,2 @@
|
|||||||
docgen:
|
docgen:
|
||||||
data_dump_email:
|
# No ICU messages needed for data_dump_email anymore
|
||||||
link_valid_until: >-
|
|
||||||
Ce lien est valide jusqu'au {validity, date, full}, {validity, time, medium}
|
|
||||||
|
@ -34,8 +34,10 @@ docgen:
|
|||||||
data_dump_email:
|
data_dump_email:
|
||||||
subject: Contenu des données de génération de document disponible
|
subject: Contenu des données de génération de document disponible
|
||||||
Dear: Cher
|
Dear: Cher
|
||||||
data_dump_ready_and_link: >-
|
data_dump_ready_and_attached: >-
|
||||||
Le contenu des données est disponible. Vous pouvez le télécharger à l'aide du lien suivant:
|
Le contenu des données est disponible. Vous le trouverez en pièce jointe à cet email.
|
||||||
|
filename: >-
|
||||||
|
Nom du fichier: %filename%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user