mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-01 04:49:36 +00:00
Compare commits
8 commits
43d72faf48
...
233c5e8550
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
233c5e8550 | ||
|
|
6b83c772cc | ||
|
|
1996db6a53 | ||
|
|
f69b0889eb | ||
|
|
c8b1320bb9 | ||
|
|
e11cb7d5cb | ||
|
|
097041a43a | ||
|
|
b21d294cf8 |
195 changed files with 743 additions and 658 deletions
|
|
@ -20,12 +20,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Symfony\Config\DoctrineConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+.
|
* This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+.
|
||||||
* We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version.
|
* We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return static function(\Symfony\Config\DoctrineConfig $doctrine) {
|
return static function(DoctrineConfig $doctrine) {
|
||||||
//On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
|
//On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
|
||||||
if (PHP_VERSION_ID >= 80400) {
|
if (PHP_VERSION_ID >= 80400) {
|
||||||
$doctrine->orm()->enableNativeLazyObjects(true);
|
$doctrine->orm()->enableNativeLazyObjects(true);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use Rector\Symfony\Set\SymfonySetList;
|
||||||
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
|
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
|
||||||
|
|
||||||
return RectorConfig::configure()
|
return RectorConfig::configure()
|
||||||
->withComposerBased(phpunit: true)
|
->withComposerBased(phpunit: true, symfony: true)
|
||||||
|
|
||||||
->withSymfonyContainerPhp(__DIR__ . '/tests/symfony-container.php')
|
->withSymfonyContainerPhp(__DIR__ . '/tests/symfony-container.php')
|
||||||
->withSymfonyContainerXml(__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml')
|
->withSymfonyContainerXml(__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml')
|
||||||
|
|
@ -36,8 +36,6 @@ return RectorConfig::configure()
|
||||||
PHPUnitSetList::PHPUNIT_90,
|
PHPUnitSetList::PHPUNIT_90,
|
||||||
PHPUnitSetList::PHPUNIT_110,
|
PHPUnitSetList::PHPUNIT_110,
|
||||||
PHPUnitSetList::PHPUNIT_CODE_QUALITY,
|
PHPUnitSetList::PHPUNIT_CODE_QUALITY,
|
||||||
|
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
->withRules([
|
->withRules([
|
||||||
|
|
@ -59,6 +57,9 @@ return RectorConfig::configure()
|
||||||
PreferPHPUnitThisCallRector::class,
|
PreferPHPUnitThisCallRector::class,
|
||||||
//Do not replace 'GET' with class constant,
|
//Do not replace 'GET' with class constant,
|
||||||
LiteralGetToRequestClassConstantRector::class,
|
LiteralGetToRequestClassConstantRector::class,
|
||||||
|
|
||||||
|
//Do not move help text of commands to the command class, as we want to keep the help text in the command definition for better readability
|
||||||
|
\Rector\Symfony\Symfony73\Rector\Class_\CommandHelpToAttributeRector::class
|
||||||
])
|
])
|
||||||
|
|
||||||
//Do not apply rules to Symfony own files
|
//Do not apply rules to Symfony own files
|
||||||
|
|
@ -67,6 +68,7 @@ return RectorConfig::configure()
|
||||||
__DIR__ . '/src/Kernel.php',
|
__DIR__ . '/src/Kernel.php',
|
||||||
__DIR__ . '/config/preload.php',
|
__DIR__ . '/config/preload.php',
|
||||||
__DIR__ . '/config/bundles.php',
|
__DIR__ . '/config/bundles.php',
|
||||||
|
__DIR__ . '/config/reference.php'
|
||||||
])
|
])
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob;
|
||||||
use App\DataTables\LogDataTable;
|
use App\DataTables\LogDataTable;
|
||||||
use App\Entity\Attachments\AttachmentUpload;
|
use App\Entity\Attachments\AttachmentUpload;
|
||||||
use App\Entity\Parts\Category;
|
use App\Entity\Parts\Category;
|
||||||
|
|
@ -151,7 +152,7 @@ final class PartController extends AbstractController
|
||||||
$jobId = $request->query->get('jobId');
|
$jobId = $request->query->get('jobId');
|
||||||
$bulkJob = null;
|
$bulkJob = null;
|
||||||
if ($jobId) {
|
if ($jobId) {
|
||||||
$bulkJob = $this->em->getRepository(\App\Entity\InfoProviderSystem\BulkInfoProviderImportJob::class)->find($jobId);
|
$bulkJob = $this->em->getRepository(BulkInfoProviderImportJob::class)->find($jobId);
|
||||||
// Verify user owns this job
|
// Verify user owns this job
|
||||||
if ($bulkJob && $bulkJob->getCreatedBy() !== $this->getUser()) {
|
if ($bulkJob && $bulkJob->getCreatedBy() !== $this->getUser()) {
|
||||||
$bulkJob = null;
|
$bulkJob = null;
|
||||||
|
|
@ -172,7 +173,7 @@ final class PartController extends AbstractController
|
||||||
throw $this->createAccessDeniedException('Invalid CSRF token');
|
throw $this->createAccessDeniedException('Invalid CSRF token');
|
||||||
}
|
}
|
||||||
|
|
||||||
$bulkJob = $this->em->getRepository(\App\Entity\InfoProviderSystem\BulkInfoProviderImportJob::class)->find($jobId);
|
$bulkJob = $this->em->getRepository(BulkInfoProviderImportJob::class)->find($jobId);
|
||||||
if (!$bulkJob || $bulkJob->getCreatedBy() !== $this->getUser()) {
|
if (!$bulkJob || $bulkJob->getCreatedBy() !== $this->getUser()) {
|
||||||
throw $this->createNotFoundException('Bulk import job not found');
|
throw $this->createNotFoundException('Bulk import job not found');
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +339,7 @@ final class PartController extends AbstractController
|
||||||
$jobId = $request->query->get('jobId');
|
$jobId = $request->query->get('jobId');
|
||||||
$bulkJob = null;
|
$bulkJob = null;
|
||||||
if ($jobId) {
|
if ($jobId) {
|
||||||
$bulkJob = $this->em->getRepository(\App\Entity\InfoProviderSystem\BulkInfoProviderImportJob::class)->find($jobId);
|
$bulkJob = $this->em->getRepository(BulkInfoProviderImportJob::class)->find($jobId);
|
||||||
// Verify user owns this job
|
// Verify user owns this job
|
||||||
if ($bulkJob && $bulkJob->getCreatedBy() !== $this->getUser()) {
|
if ($bulkJob && $bulkJob->getCreatedBy() !== $this->getUser()) {
|
||||||
$bulkJob = null;
|
$bulkJob = null;
|
||||||
|
|
|
||||||
|
|
@ -147,10 +147,7 @@ class SecurityController extends AbstractController
|
||||||
'label' => 'user.settings.pw_confirm.label',
|
'label' => 'user.settings.pw_confirm.label',
|
||||||
],
|
],
|
||||||
'invalid_message' => 'password_must_match',
|
'invalid_message' => 'password_must_match',
|
||||||
'constraints' => [new Length([
|
'constraints' => [new Length(min: 6, max: 128)],
|
||||||
'min' => 6,
|
|
||||||
'max' => 128,
|
|
||||||
])],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->add('submit', SubmitType::class, [
|
$builder->add('submit', SubmitType::class, [
|
||||||
|
|
|
||||||
|
|
@ -295,10 +295,7 @@ class UserSettingsController extends AbstractController
|
||||||
'autocomplete' => 'new-password',
|
'autocomplete' => 'new-password',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'constraints' => [new Length([
|
'constraints' => [new Length(min: 6, max: 128)],
|
||||||
'min' => 6,
|
|
||||||
'max' => 128,
|
|
||||||
])],
|
|
||||||
])
|
])
|
||||||
->add('submit', SubmitType::class, [
|
->add('submit', SubmitType::class, [
|
||||||
'label' => 'save',
|
'label' => 'save',
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ class PartSearchFilter implements FilterInterface
|
||||||
if ($search_dbId) {
|
if ($search_dbId) {
|
||||||
$expressions[] = $queryBuilder->expr()->eq('part.id', ':id_exact');
|
$expressions[] = $queryBuilder->expr()->eq('part.id', ':id_exact');
|
||||||
$queryBuilder->setParameter('id_exact', (int) $this->keyword,
|
$queryBuilder->setParameter('id_exact', (int) $this->keyword,
|
||||||
\Doctrine\DBAL\ParameterType::INTEGER);
|
ParameterType::INTEGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Guard condition
|
//Guard condition
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\EventSubscriber\UserSystem;
|
namespace App\EventSubscriber\UserSystem;
|
||||||
|
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,7 @@ class AttachmentFormType extends AbstractType
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
//new AllowedFileExtension(),
|
//new AllowedFileExtension(),
|
||||||
new File([
|
new File(maxSize: $options['max_file_size']),
|
||||||
'maxSize' => $options['max_file_size'],
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Form\InfoProviderSystem;
|
namespace App\Form\InfoProviderSystem;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraints\Range;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
|
|
@ -61,7 +62,7 @@ class FieldToProviderMappingType extends AbstractType
|
||||||
'style' => 'width: 80px;'
|
'style' => 'width: 80px;'
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
new \Symfony\Component\Validator\Constraints\Range(['min' => 1, 'max' => 10]),
|
new Range(min: 1, max: 10),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,10 +177,7 @@ class UserAdminForm extends AbstractType
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'disabled' => !$this->security->isGranted('set_password', $entity) || $entity->isSamlUser(),
|
'disabled' => !$this->security->isGranted('set_password', $entity) || $entity->isSamlUser(),
|
||||||
'constraints' => [new Length([
|
'constraints' => [new Length(min: 6, max: 128)],
|
||||||
'min' => 6,
|
|
||||||
'max' => 128,
|
|
||||||
])],
|
|
||||||
])
|
])
|
||||||
|
|
||||||
->add('need_pw_change', CheckboxType::class, [
|
->add('need_pw_change', CheckboxType::class, [
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,7 @@ class UserSettingsType extends AbstractType
|
||||||
'accept' => 'image/*',
|
'accept' => 'image/*',
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
new File([
|
new File(maxSize: '5M'),
|
||||||
'maxSize' => '5M',
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
->add('aboutMe', RichTextEditorType::class, [
|
->add('aboutMe', RichTextEditorType::class, [
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
namespace App\Services\ImportExportSystem;
|
namespace App\Services\ImportExportSystem;
|
||||||
|
|
||||||
|
use App\Entity\Parts\Supplier;
|
||||||
|
use App\Entity\PriceInformations\Orderdetail;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\ProjectSystem\Project;
|
use App\Entity\ProjectSystem\Project;
|
||||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||||
|
|
@ -275,7 +277,7 @@ class BOMImporter
|
||||||
$mapped_entries = []; // Collect all mapped entries for validation
|
$mapped_entries = []; // Collect all mapped entries for validation
|
||||||
|
|
||||||
// Fetch suppliers once for efficiency
|
// Fetch suppliers once for efficiency
|
||||||
$suppliers = $this->entityManager->getRepository(\App\Entity\Parts\Supplier::class)->findAll();
|
$suppliers = $this->entityManager->getRepository(Supplier::class)->findAll();
|
||||||
$supplierSPNKeys = [];
|
$supplierSPNKeys = [];
|
||||||
$suppliersByName = []; // Map supplier names to supplier objects
|
$suppliersByName = []; // Map supplier names to supplier objects
|
||||||
foreach ($suppliers as $supplier) {
|
foreach ($suppliers as $supplier) {
|
||||||
|
|
@ -371,7 +373,7 @@ class BOMImporter
|
||||||
|
|
||||||
if ($supplier_spn !== null) {
|
if ($supplier_spn !== null) {
|
||||||
// Query for orderdetails with matching supplier and SPN
|
// Query for orderdetails with matching supplier and SPN
|
||||||
$orderdetail = $this->entityManager->getRepository(\App\Entity\PriceInformations\Orderdetail::class)
|
$orderdetail = $this->entityManager->getRepository(Orderdetail::class)
|
||||||
->findOneBy([
|
->findOneBy([
|
||||||
'supplier' => $supplier,
|
'supplier' => $supplier,
|
||||||
'supplierpartnr' => $supplier_spn,
|
'supplierpartnr' => $supplier_spn,
|
||||||
|
|
@ -535,7 +537,7 @@ class BOMImporter
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add dynamic supplier fields based on available suppliers in the database
|
// Add dynamic supplier fields based on available suppliers in the database
|
||||||
$suppliers = $this->entityManager->getRepository(\App\Entity\Parts\Supplier::class)->findAll();
|
$suppliers = $this->entityManager->getRepository(Supplier::class)->findAll();
|
||||||
foreach ($suppliers as $supplier) {
|
foreach ($suppliers as $supplier) {
|
||||||
$supplierName = $supplier->getName();
|
$supplierName = $supplier->getName();
|
||||||
$targets[$supplierName . ' SPN'] = [
|
$targets[$supplierName . ' SPN'] = [
|
||||||
|
|
@ -570,7 +572,7 @@ class BOMImporter
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add supplier-specific patterns
|
// Add supplier-specific patterns
|
||||||
$suppliers = $this->entityManager->getRepository(\App\Entity\Parts\Supplier::class)->findAll();
|
$suppliers = $this->entityManager->getRepository(Supplier::class)->findAll();
|
||||||
foreach ($suppliers as $supplier) {
|
foreach ($suppliers as $supplier) {
|
||||||
$supplierName = $supplier->getName();
|
$supplierName = $supplier->getName();
|
||||||
$supplierLower = strtolower($supplierName);
|
$supplierLower = strtolower($supplierName);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\ImportExportSystem;
|
namespace App\Services\ImportExportSystem;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
use App\Entity\Base\AbstractNamedDBElement;
|
use App\Entity\Base\AbstractNamedDBElement;
|
||||||
use App\Entity\Base\AbstractStructuralDBElement;
|
use App\Entity\Base\AbstractStructuralDBElement;
|
||||||
use App\Helpers\FilenameSanatizer;
|
use App\Helpers\FilenameSanatizer;
|
||||||
|
|
@ -177,7 +178,7 @@ class EntityExporter
|
||||||
$colIndex = 1;
|
$colIndex = 1;
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
$cellCoordinate = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
|
$cellCoordinate = Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
|
||||||
$worksheet->setCellValue($cellCoordinate, $column);
|
$worksheet->setCellValue($cellCoordinate, $column);
|
||||||
$colIndex++;
|
$colIndex++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\ImportExportSystem;
|
namespace App\Services\ImportExportSystem;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
use App\Entity\Base\AbstractNamedDBElement;
|
use App\Entity\Base\AbstractNamedDBElement;
|
||||||
use App\Entity\Base\AbstractStructuralDBElement;
|
use App\Entity\Base\AbstractStructuralDBElement;
|
||||||
use App\Entity\Parts\Category;
|
use App\Entity\Parts\Category;
|
||||||
|
|
@ -419,14 +420,14 @@ class EntityImporter
|
||||||
'worksheet_title' => $worksheet->getTitle()
|
'worksheet_title' => $worksheet->getTitle()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
|
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
|
||||||
|
|
||||||
for ($row = 1; $row <= $highestRow; $row++) {
|
for ($row = 1; $row <= $highestRow; $row++) {
|
||||||
$rowData = [];
|
$rowData = [];
|
||||||
|
|
||||||
// Read all columns using numeric index
|
// Read all columns using numeric index
|
||||||
for ($colIndex = 1; $colIndex <= $highestColumnIndex; $colIndex++) {
|
for ($colIndex = 1; $colIndex <= $highestColumnIndex; $colIndex++) {
|
||||||
$col = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colIndex);
|
$col = Coordinate::stringFromColumnIndex($colIndex);
|
||||||
try {
|
try {
|
||||||
$cellValue = $worksheet->getCell("{$col}{$row}")->getCalculatedValue();
|
$cellValue = $worksheet->getCell("{$col}{$row}")->getCalculatedValue();
|
||||||
$rowData[] = $cellValue ?? '';
|
$rowData[] = $cellValue ?? '';
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\InfoProviderSystem\DTOs;
|
namespace App\Services\InfoProviderSystem\DTOs;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Exception\ORMException;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Traversable;
|
use Traversable;
|
||||||
|
|
@ -176,7 +177,7 @@ readonly class BulkSearchResponseDTO implements \ArrayAccess, \IteratorAggregate
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param EntityManagerInterface $entityManager
|
* @param EntityManagerInterface $entityManager
|
||||||
* @return BulkSearchResponseDTO
|
* @return BulkSearchResponseDTO
|
||||||
* @throws \Doctrine\ORM\Exception\ORMException
|
* @throws ORMException
|
||||||
*/
|
*/
|
||||||
public static function fromSerializableRepresentation(array $data, EntityManagerInterface $entityManager): BulkSearchResponseDTO
|
public static function fromSerializableRepresentation(array $data, EntityManagerInterface $entityManager): BulkSearchResponseDTO
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ class BuerklinProvider implements BatchInfoProviderInterface, URLHandlerInfoProv
|
||||||
* - prefers 'zoom' format, then 'product' format, then all others
|
* - prefers 'zoom' format, then 'product' format, then all others
|
||||||
*
|
*
|
||||||
* @param array|null $images
|
* @param array|null $images
|
||||||
* @return \App\Services\InfoProviderSystem\DTOs\FileDTO[]
|
* @return FileDTO[]
|
||||||
*/
|
*/
|
||||||
private function getProductImages(?array $images): array
|
private function getProductImages(?array $images): array
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,14 @@ use App\Twig\Sandbox\SandboxedLabelExtension;
|
||||||
use App\Twig\TwigCoreExtension;
|
use App\Twig\TwigCoreExtension;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
|
use Twig\Extension\AttributeExtension;
|
||||||
use Twig\Extension\SandboxExtension;
|
use Twig\Extension\SandboxExtension;
|
||||||
use Twig\Extra\Html\HtmlExtension;
|
use Twig\Extra\Html\HtmlExtension;
|
||||||
use Twig\Extra\Intl\IntlExtension;
|
use Twig\Extra\Intl\IntlExtension;
|
||||||
use Twig\Extra\Markdown\MarkdownExtension;
|
use Twig\Extra\Markdown\MarkdownExtension;
|
||||||
use Twig\Extra\String\StringExtension;
|
use Twig\Extra\String\StringExtension;
|
||||||
use Twig\Loader\ArrayLoader;
|
use Twig\Loader\ArrayLoader;
|
||||||
|
use Twig\RuntimeLoader\FactoryRuntimeLoader;
|
||||||
use Twig\Sandbox\SecurityPolicyInterface;
|
use Twig\Sandbox\SecurityPolicyInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,13 +188,18 @@ final class SandboxedTwigFactory
|
||||||
$twig->addExtension(new StringExtension());
|
$twig->addExtension(new StringExtension());
|
||||||
$twig->addExtension(new HtmlExtension());
|
$twig->addExtension(new HtmlExtension());
|
||||||
|
|
||||||
//Add Part-DB specific extension
|
|
||||||
$twig->addExtension($this->formatExtension);
|
|
||||||
$twig->addExtension($this->barcodeExtension);
|
|
||||||
$twig->addExtension($this->entityExtension);
|
|
||||||
$twig->addExtension($this->twigCoreExtension);
|
|
||||||
$twig->addExtension($this->sandboxedLabelExtension);
|
$twig->addExtension($this->sandboxedLabelExtension);
|
||||||
|
|
||||||
|
//Our other extensions are using attributes, we need a bit more work to load them
|
||||||
|
$dynamicExtensions = [$this->formatExtension, $this->barcodeExtension, $this->entityExtension, $this->twigCoreExtension];
|
||||||
|
$dynamicExtensionsMap = [];
|
||||||
|
|
||||||
|
foreach ($dynamicExtensions as $extension) {
|
||||||
|
$twig->addExtension(new AttributeExtension($extension::class));
|
||||||
|
$dynamicExtensionsMap[$extension::class] = static fn () => $extension;
|
||||||
|
}
|
||||||
|
$twig->addRuntimeLoader(new FactoryRuntimeLoader($dynamicExtensionsMap));
|
||||||
|
|
||||||
return $twig;
|
return $twig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class LogDiffFormatter
|
||||||
* @param $old_data
|
* @param $old_data
|
||||||
* @param $new_data
|
* @param $new_data
|
||||||
*/
|
*/
|
||||||
public function formatDiff($old_data, $new_data): string
|
public function formatDiff(mixed $old_data, mixed $new_data): string
|
||||||
{
|
{
|
||||||
if (is_string($old_data) && is_string($new_data)) {
|
if (is_string($old_data) && is_string($new_data)) {
|
||||||
return $this->diffString($old_data, $new_data);
|
return $this->diffString($old_data, $new_data);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\System;
|
namespace App\Services\System;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
|
||||||
|
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shivas\VersioningBundle\Service\VersionManagerInterface;
|
use Shivas\VersioningBundle\Service\VersionManagerInterface;
|
||||||
|
|
@ -334,7 +336,7 @@ readonly class BackupManager
|
||||||
$params = $connection->getParams();
|
$params = $connection->getParams();
|
||||||
$platform = $connection->getDatabasePlatform();
|
$platform = $connection->getDatabasePlatform();
|
||||||
|
|
||||||
if ($platform instanceof \Doctrine\DBAL\Platforms\AbstractMySQLPlatform) {
|
if ($platform instanceof AbstractMySQLPlatform) {
|
||||||
// Use mysql command to import - need to use shell to handle input redirection
|
// Use mysql command to import - need to use shell to handle input redirection
|
||||||
$mysqlCmd = 'mysql';
|
$mysqlCmd = 'mysql';
|
||||||
if (isset($params['host'])) {
|
if (isset($params['host'])) {
|
||||||
|
|
@ -361,7 +363,7 @@ readonly class BackupManager
|
||||||
if (!$process->isSuccessful()) {
|
if (!$process->isSuccessful()) {
|
||||||
throw new \RuntimeException('MySQL import failed: ' . $process->getErrorOutput());
|
throw new \RuntimeException('MySQL import failed: ' . $process->getErrorOutput());
|
||||||
}
|
}
|
||||||
} elseif ($platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform) {
|
} elseif ($platform instanceof PostgreSQLPlatform) {
|
||||||
// Use psql command to import
|
// Use psql command to import
|
||||||
$psqlCmd = 'psql';
|
$psqlCmd = 'psql';
|
||||||
if (isset($params['host'])) {
|
if (isset($params['host'])) {
|
||||||
|
|
|
||||||
|
|
@ -25,22 +25,34 @@ namespace App\Twig;
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
use App\Services\Attachments\AttachmentURLGenerator;
|
use App\Services\Attachments\AttachmentURLGenerator;
|
||||||
use App\Services\Misc\FAIconGenerator;
|
use App\Services\Misc\FAIconGenerator;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
final class AttachmentExtension extends AbstractExtension
|
final readonly class AttachmentExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected AttachmentURLGenerator $attachmentURLGenerator, protected FAIconGenerator $FAIconGenerator)
|
public function __construct(private AttachmentURLGenerator $attachmentURLGenerator, private FAIconGenerator $FAIconGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
/**
|
||||||
|
* Returns the URL of the thumbnail of the given attachment. Returns null if no thumbnail is available.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("attachment_thumbnail")]
|
||||||
|
public function attachmentThumbnail(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string
|
||||||
{
|
{
|
||||||
return [
|
return $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name);
|
||||||
/* Returns the URL to a thumbnail of the given attachment */
|
}
|
||||||
new TwigFunction('attachment_thumbnail', fn(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string => $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name)),
|
|
||||||
/* Returns the font awesome icon class which is representing the given file extension (We allow null here for attachments without extension) */
|
/**
|
||||||
new TwigFunction('ext_to_fa_icon', fn(?string $extension): string => $this->FAIconGenerator->fileExtensionToFAType($extension ?? '')),
|
* Return the font-awesome icon type for the given file extension. Returns "file" if no specific icon is available.
|
||||||
];
|
* Null is allowed for files withot extension
|
||||||
|
* @param string|null $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("ext_to_fa_icon")]
|
||||||
|
public function extToFAIcon(?string $extension): string
|
||||||
|
{
|
||||||
|
return $this->FAIconGenerator->fileExtensionToFAType($extension ?? '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,19 +23,14 @@ declare(strict_types=1);
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use Com\Tecnick\Barcode\Barcode;
|
use Com\Tecnick\Barcode\Barcode;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\TwigFunction;
|
|
||||||
|
|
||||||
final class BarcodeExtension extends AbstractExtension
|
final class BarcodeExtension
|
||||||
{
|
{
|
||||||
public function getFunctions(): array
|
/**
|
||||||
{
|
* Generates a barcode in SVG format for the given content and type.
|
||||||
return [
|
*/
|
||||||
/* Generates a barcode with the given Type and Data and returns it as an SVG represenation */
|
#[AsTwigFunction('barcode_svg')]
|
||||||
new TwigFunction('barcode_svg', fn(string $content, string $type = 'QRCODE'): string => $this->barcodeSVG($content, $type)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function barcodeSVG(string $content, string $type = 'QRCODE'): string
|
public function barcodeSVG(string $content, string $type = 'QRCODE'): string
|
||||||
{
|
{
|
||||||
$barcodeFactory = new Barcode();
|
$barcodeFactory = new Barcode();
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@ use App\Exceptions\EntityNotSupportedException;
|
||||||
use App\Services\ElementTypeNameGenerator;
|
use App\Services\ElementTypeNameGenerator;
|
||||||
use App\Services\EntityURLGenerator;
|
use App\Services\EntityURLGenerator;
|
||||||
use App\Services\Trees\TreeViewGenerator;
|
use App\Services\Trees\TreeViewGenerator;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
|
use Twig\Attribute\AsTwigTest;
|
||||||
|
use Twig\DeprecatedCallableInfo;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
use Twig\TwigTest;
|
use Twig\TwigTest;
|
||||||
|
|
@ -48,61 +51,27 @@ use Twig\TwigTest;
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Twig\EntityExtensionTest
|
* @see \App\Tests\Twig\EntityExtensionTest
|
||||||
*/
|
*/
|
||||||
final class EntityExtension extends AbstractExtension
|
final readonly class EntityExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected EntityURLGenerator $entityURLGenerator, protected TreeViewGenerator $treeBuilder, private readonly ElementTypeNameGenerator $nameGenerator)
|
public function __construct(private EntityURLGenerator $entityURLGenerator, private TreeViewGenerator $treeBuilder, private ElementTypeNameGenerator $nameGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTests(): array
|
/**
|
||||||
|
* Checks if the given variable is an entity (instance of AbstractDBElement).
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("entity")]
|
||||||
|
public function entityTest(mixed $var): bool
|
||||||
{
|
{
|
||||||
return [
|
return $var instanceof AbstractDBElement;
|
||||||
/* Checks if the given variable is an entitity (instance of AbstractDBElement) */
|
|
||||||
new TwigTest('entity', static fn($var) => $var instanceof AbstractDBElement),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
/* Returns a string representation of the given entity */
|
|
||||||
new TwigFunction('entity_type', fn(object $entity): ?string => $this->getEntityType($entity)),
|
|
||||||
/* Returns the URL to the given entity */
|
|
||||||
new TwigFunction('entity_url', fn(AbstractDBElement $entity, string $method = 'info'): string => $this->generateEntityURL($entity, $method)),
|
|
||||||
/* Returns the URL to the given entity in timetravel mode */
|
|
||||||
new TwigFunction('timetravel_url', fn(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string => $this->timeTravelURL($element, $dateTime)),
|
|
||||||
/* Generates a JSON array of the given tree */
|
|
||||||
new TwigFunction('tree_data', fn(AbstractDBElement $element, string $type = 'newEdit'): string => $this->treeData($element, $type)),
|
|
||||||
|
|
||||||
/* Gets a human readable label for the type of the given entity */
|
/**
|
||||||
new TwigFunction('entity_type_label', fn(object|string $entity): string => $this->nameGenerator->getLocalizedTypeLabel($entity)),
|
* Returns a string representation of the given entity
|
||||||
new TwigFunction('type_label', fn(object|string $entity): string => $this->nameGenerator->typeLabel($entity)),
|
*/
|
||||||
new TwigFunction('type_label_p', fn(object|string $entity): string => $this->nameGenerator->typeLabelPlural($entity)),
|
#[AsTwigFunction("entity_type")]
|
||||||
];
|
public function entityType(object $entity): ?string
|
||||||
}
|
|
||||||
|
|
||||||
public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->entityURLGenerator->timeTravelURL($element, $dateTime);
|
|
||||||
} catch (EntityNotSupportedException) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string
|
|
||||||
{
|
|
||||||
$tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element);
|
|
||||||
|
|
||||||
return json_encode($tree, JSON_THROW_ON_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateEntityURL(AbstractDBElement $entity, string $method = 'info'): string
|
|
||||||
{
|
|
||||||
return $this->entityURLGenerator->getURL($entity, $method);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEntityType(object $entity): ?string
|
|
||||||
{
|
{
|
||||||
$map = [
|
$map = [
|
||||||
Part::class => 'part',
|
Part::class => 'part',
|
||||||
|
|
@ -129,4 +98,69 @@ final class EntityExtension extends AbstractExtension
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL for the given entity and method. E.g. for a Part and method "edit", it will return the URL to edit this part.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("entity_url")]
|
||||||
|
public function entityURL(AbstractDBElement $entity, string $method = 'info'): string
|
||||||
|
{
|
||||||
|
return $this->entityURLGenerator->getURL($entity, $method);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL for the given entity in timetravel mode.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("timetravel_url")]
|
||||||
|
public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->entityURLGenerator->timeTravelURL($element, $dateTime);
|
||||||
|
} catch (EntityNotSupportedException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a tree data structure for the given element, which can be used to display a tree view of the element and its related entities.
|
||||||
|
* The type parameter can be used to specify the type of tree view (e.g. "newEdit" for the tree view in the new/edit pages). The returned data is a JSON string.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("tree_data")]
|
||||||
|
public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string
|
||||||
|
{
|
||||||
|
$tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element);
|
||||||
|
|
||||||
|
return json_encode($tree, JSON_THROW_ON_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German.
|
||||||
|
* @deprecated Use the "type_label" function instead, which does the same but is more concise.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("entity_type_label", deprecationInfo: new DeprecatedCallableInfo("Part-DB", "2", "Use the 'type_label' function instead."))]
|
||||||
|
public function entityTypeLabel(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->getLocalizedTypeLabel($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("type_label")]
|
||||||
|
public function typeLabel(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->typeLabel($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized plural type label for the given entity. E.g. for a Part, it will return "Parts" in English and "Bauteile" in German.
|
||||||
|
* @param object|string $entity
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("type_label_p")]
|
||||||
|
public function typeLabelPlural(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->typeLabelPlural($entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,35 +29,28 @@ use App\Services\Formatters\MarkdownParser;
|
||||||
use App\Services\Formatters\MoneyFormatter;
|
use App\Services\Formatters\MoneyFormatter;
|
||||||
use App\Services\Formatters\SIFormatter;
|
use App\Services\Formatters\SIFormatter;
|
||||||
use Brick\Math\BigDecimal;
|
use Brick\Math\BigDecimal;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Attribute\AsTwigFilter;
|
||||||
use Twig\TwigFilter;
|
|
||||||
|
|
||||||
final class FormatExtension extends AbstractExtension
|
final readonly class FormatExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected MarkdownParser $markdownParser, protected MoneyFormatter $moneyFormatter, protected SIFormatter $siformatter, protected AmountFormatter $amountFormatter)
|
public function __construct(private MarkdownParser $markdownParser, private MoneyFormatter $moneyFormatter, private SIFormatter $siformatter, private AmountFormatter $amountFormatter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
/**
|
||||||
|
* Mark the given text as markdown, which will be rendered in the browser
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_markdown", isSafe: ['html'], preEscape: 'html')]
|
||||||
|
public function formatMarkdown(string $markdown, bool $inline_mode = false): string
|
||||||
{
|
{
|
||||||
return [
|
return $this->markdownParser->markForRendering($markdown, $inline_mode);
|
||||||
/* Mark the given text as markdown, which will be rendered in the browser */
|
|
||||||
new TwigFilter('format_markdown', fn(string $markdown, bool $inline_mode = false): string => $this->markdownParser->markForRendering($markdown, $inline_mode), [
|
|
||||||
'pre_escape' => 'html',
|
|
||||||
'is_safe' => ['html'],
|
|
||||||
]),
|
|
||||||
/* Format the given amount as money, using a given currency */
|
|
||||||
new TwigFilter('format_money', fn($amount, ?Currency $currency = null, int $decimals = 5): string => $this->formatCurrency($amount, $currency, $decimals)),
|
|
||||||
/* Format the given number using SI prefixes and the given unit (string) */
|
|
||||||
new TwigFilter('format_si', fn($value, $unit, $decimals = 2, bool $show_all_digits = false): string => $this->siFormat($value, $unit, $decimals, $show_all_digits)),
|
|
||||||
/** Format the given amount using the given MeasurementUnit */
|
|
||||||
new TwigFilter('format_amount', fn($value, ?MeasurementUnit $unit, array $options = []): string => $this->amountFormat($value, $unit, $options)),
|
|
||||||
/** Format the given number of bytes as human-readable number */
|
|
||||||
new TwigFilter('format_bytes', fn(int $bytes, int $precision = 2): string => $this->formatBytes($bytes, $precision)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formatCurrency($amount, ?Currency $currency = null, int $decimals = 5): string
|
/**
|
||||||
|
* Format the given amount as money, using a given currency
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_money")]
|
||||||
|
public function formatMoney(BigDecimal|float|string $amount, ?Currency $currency = null, int $decimals = 5): string
|
||||||
{
|
{
|
||||||
if ($amount instanceof BigDecimal) {
|
if ($amount instanceof BigDecimal) {
|
||||||
$amount = (string) $amount;
|
$amount = (string) $amount;
|
||||||
|
|
@ -66,19 +59,22 @@ final class FormatExtension extends AbstractExtension
|
||||||
return $this->moneyFormatter->format($amount, $currency, $decimals);
|
return $this->moneyFormatter->format($amount, $currency, $decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function siFormat($value, $unit, $decimals = 2, bool $show_all_digits = false): string
|
/**
|
||||||
|
* Format the given number using SI prefixes and the given unit (string)
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_si")]
|
||||||
|
public function siFormat(float $value, string $unit, int $decimals = 2, bool $show_all_digits = false): string
|
||||||
{
|
{
|
||||||
return $this->siformatter->format($value, $unit, $decimals);
|
return $this->siformatter->format($value, $unit, $decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function amountFormat($value, ?MeasurementUnit $unit, array $options = []): string
|
#[AsTwigFilter("format_amount")]
|
||||||
|
public function amountFormat(float|int|string $value, ?MeasurementUnit $unit, array $options = []): string
|
||||||
{
|
{
|
||||||
return $this->amountFormatter->format($value, $unit, $options);
|
return $this->amountFormatter->format($value, $unit, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[AsTwigFilter("format_bytes")]
|
||||||
* @param $bytes
|
|
||||||
*/
|
|
||||||
public function formatBytes(int $bytes, int $precision = 2): string
|
public function formatBytes(int $bytes, int $precision = 2): string
|
||||||
{
|
{
|
||||||
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||||
|
|
|
||||||
|
|
@ -23,31 +23,25 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use App\Services\InfoProviderSystem\ProviderRegistry;
|
use App\Services\InfoProviderSystem\ProviderRegistry;
|
||||||
use App\Services\InfoProviderSystem\Providers\InfoProviderInterface;
|
use App\Services\InfoProviderSystem\Providers\InfoProviderInterface;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
class InfoProviderExtension extends AbstractExtension
|
final readonly class InfoProviderExtension
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ProviderRegistry $providerRegistry
|
private ProviderRegistry $providerRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getFunctions(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
new TwigFunction('info_provider', $this->getInfoProvider(...)),
|
|
||||||
new TwigFunction('info_provider_label', $this->getInfoProviderName(...))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the info provider with the given key. Returns null, if the provider does not exist.
|
* Gets the info provider with the given key. Returns null, if the provider does not exist.
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return InfoProviderInterface|null
|
* @return InfoProviderInterface|null
|
||||||
*/
|
*/
|
||||||
private function getInfoProvider(string $key): ?InfoProviderInterface
|
#[AsTwigFunction(name: 'info_provider')]
|
||||||
|
public function getInfoProvider(string $key): ?InfoProviderInterface
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->providerRegistry->getProviderByKey($key);
|
return $this->providerRegistry->getProviderByKey($key);
|
||||||
|
|
@ -61,7 +55,8 @@ class InfoProviderExtension extends AbstractExtension
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private function getInfoProviderName(string $key): ?string
|
#[AsTwigFunction(name: 'info_provider_label')]
|
||||||
|
public function getInfoProviderName(string $key): ?string
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->providerRegistry->getProviderByKey($key)->getProviderInfo()['name'];
|
return $this->providerRegistry->getProviderByKey($key)->getProviderInfo()['name'];
|
||||||
|
|
@ -69,4 +64,4 @@ class InfoProviderExtension extends AbstractExtension
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,26 @@ namespace App\Twig;
|
||||||
use App\Entity\LogSystem\AbstractLogEntry;
|
use App\Entity\LogSystem\AbstractLogEntry;
|
||||||
use App\Services\LogSystem\LogDataFormatter;
|
use App\Services\LogSystem\LogDataFormatter;
|
||||||
use App\Services\LogSystem\LogDiffFormatter;
|
use App\Services\LogSystem\LogDiffFormatter;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
final class LogExtension extends AbstractExtension
|
final readonly class LogExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(private readonly LogDataFormatter $logDataFormatter, private readonly LogDiffFormatter $logDiffFormatter)
|
public function __construct(private LogDataFormatter $logDataFormatter, private LogDiffFormatter $logDiffFormatter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
#[AsTwigFunction(name: 'format_log_data', isSafe: ['html'])]
|
||||||
|
public function formatLogData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string
|
||||||
{
|
{
|
||||||
return [
|
return $this->logDataFormatter->formatData($data, $logEntry, $fieldName);
|
||||||
new TwigFunction('format_log_data', fn($data, AbstractLogEntry $logEntry, string $fieldName): string => $this->logDataFormatter->formatData($data, $logEntry, $fieldName), ['is_safe' => ['html']]),
|
}
|
||||||
new TwigFunction('format_log_diff', fn($old_data, $new_data): string => $this->logDiffFormatter->formatDiff($old_data, $new_data), ['is_safe' => ['html']]),
|
|
||||||
];
|
#[AsTwigFunction(name: 'format_log_diff', isSafe: ['html'])]
|
||||||
|
public function formatLogDiff(mixed $old_data, mixed $new_data): string
|
||||||
|
{
|
||||||
|
return $this->logDiffFormatter->formatDiff($old_data, $new_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use App\Settings\SettingsIcon;
|
use App\Settings\SettingsIcon;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use App\Services\LogSystem\EventCommentType;
|
use App\Services\LogSystem\EventCommentType;
|
||||||
|
|
@ -31,23 +32,14 @@ use Twig\TwigFunction;
|
||||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
|
||||||
final class MiscExtension extends AbstractExtension
|
final readonly class MiscExtension
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EventCommentNeededHelper $eventCommentNeededHelper)
|
public function __construct(private EventCommentNeededHelper $eventCommentNeededHelper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
#[AsTwigFunction(name: 'event_comment_needed')]
|
||||||
{
|
public function evenCommentNeeded(string|EventCommentType $operation_type): bool
|
||||||
return [
|
|
||||||
new TwigFunction('event_comment_needed', $this->evenCommentNeeded(...)),
|
|
||||||
|
|
||||||
new TwigFunction('settings_icon', $this->settingsIcon(...)),
|
|
||||||
new TwigFunction('uri_without_host', $this->uri_without_host(...))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function evenCommentNeeded(string|EventCommentType $operation_type): bool
|
|
||||||
{
|
{
|
||||||
if (is_string($operation_type)) {
|
if (is_string($operation_type)) {
|
||||||
$operation_type = EventCommentType::from($operation_type);
|
$operation_type = EventCommentType::from($operation_type);
|
||||||
|
|
@ -63,7 +55,8 @@ final class MiscExtension extends AbstractExtension
|
||||||
* @return string|null
|
* @return string|null
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
*/
|
*/
|
||||||
private function settingsIcon(string|object $objectOrClass): ?string
|
#[AsTwigFunction(name: 'settings_icon')]
|
||||||
|
public function settingsIcon(string|object $objectOrClass): ?string
|
||||||
{
|
{
|
||||||
//If the given object is a proxy, then get the real object
|
//If the given object is a proxy, then get the real object
|
||||||
if (is_a($objectOrClass, SettingsProxyInterface::class)) {
|
if (is_a($objectOrClass, SettingsProxyInterface::class)) {
|
||||||
|
|
@ -82,6 +75,7 @@ final class MiscExtension extends AbstractExtension
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFunction(name: 'uri_without_host')]
|
||||||
public function uri_without_host(Request $request): string
|
public function uri_without_host(Request $request): string
|
||||||
{
|
{
|
||||||
if (null !== $qs = $request->getQueryString()) {
|
if (null !== $qs = $request->getQueryString()) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,11 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
use Twig\Attribute\AsTwigFilter;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||||
|
use Twig\Attribute\AsTwigTest;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
@ -32,58 +36,54 @@ use Twig\TwigTest;
|
||||||
* The functionalities here extend the Twig with some core functions, which are independently of Part-DB.
|
* The functionalities here extend the Twig with some core functions, which are independently of Part-DB.
|
||||||
* @see \App\Tests\Twig\TwigCoreExtensionTest
|
* @see \App\Tests\Twig\TwigCoreExtensionTest
|
||||||
*/
|
*/
|
||||||
final class TwigCoreExtension extends AbstractExtension
|
final readonly class TwigCoreExtension
|
||||||
{
|
{
|
||||||
private readonly ObjectNormalizer $objectNormalizer;
|
private NormalizerInterface $objectNormalizer;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->objectNormalizer = new ObjectNormalizer();
|
$this->objectNormalizer = new ObjectNormalizer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
/**
|
||||||
|
* Checks if the given variable is an instance of the given class/interface/enum. E.g. `x is instanceof('App\Entity\Parts\Part')`
|
||||||
|
* @param mixed $var
|
||||||
|
* @param string $instance
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("instanceof")]
|
||||||
|
public function testInstanceOf(mixed $var, string $instance): bool
|
||||||
{
|
{
|
||||||
return [
|
if (!class_exists($instance) && !interface_exists($instance) && !enum_exists($instance)) {
|
||||||
/* Returns the enum cases as values */
|
throw new \InvalidArgumentException(sprintf('The given class/interface/enum "%s" does not exist!', $instance));
|
||||||
new TwigFunction('enum_cases', $this->getEnumCases(...)),
|
}
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTests(): array
|
return $var instanceof $instance;
|
||||||
{
|
|
||||||
return [
|
|
||||||
/*
|
|
||||||
* Checks if a given variable is an instance of a given class. E.g. ` x is instanceof('App\Entity\Parts\Part')`
|
|
||||||
*/
|
|
||||||
new TwigTest('instanceof', static fn($var, $instance) => $var instanceof $instance),
|
|
||||||
/* Checks if a given variable is an object. E.g. `x is object` */
|
|
||||||
new TwigTest('object', static fn($var): bool => is_object($var)),
|
|
||||||
new TwigTest('enum', fn($var) => $var instanceof \UnitEnum),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $enum_class
|
* Checks if the given variable is an object. This can be used to check if a variable is an object, without knowing the exact class of the object. E.g. `x is object`
|
||||||
* @phpstan-param class-string $enum_class
|
* @param mixed $var
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function getEnumCases(string $enum_class): array
|
#[AsTwigTest("object")]
|
||||||
|
public function testObject(mixed $var): bool
|
||||||
{
|
{
|
||||||
if (!enum_exists($enum_class)) {
|
return is_object($var);
|
||||||
throw new \InvalidArgumentException(sprintf('The given class "%s" is not an enum!', $enum_class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
return ($enum_class)::cases();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
/**
|
||||||
|
* Checks if the given variable is an enum (instance of UnitEnum). This can be used to check if a variable is an enum, without knowing the exact class of the enum. E.g. `x is enum`
|
||||||
|
* @param mixed $var
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("enum")]
|
||||||
|
public function testEnum(mixed $var): bool
|
||||||
{
|
{
|
||||||
return [
|
return $var instanceof \UnitEnum;
|
||||||
/* Converts the given object to an array representation of the public/accessible properties */
|
|
||||||
new TwigFilter('to_array', fn($object) => $this->toArray($object)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFilter('to_array')]
|
||||||
public function toArray(object|array $object): array
|
public function toArray(object|array $object): array
|
||||||
{
|
{
|
||||||
//If it is already an array, we can just return it
|
//If it is already an array, we can just return it
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use App\Services\System\UpdateAvailableFacade;
|
use App\Services\System\UpdateAvailableFacade;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
|
@ -31,26 +32,18 @@ use Twig\TwigFunction;
|
||||||
/**
|
/**
|
||||||
* Twig extension for update-related functions.
|
* Twig extension for update-related functions.
|
||||||
*/
|
*/
|
||||||
final class UpdateExtension extends AbstractExtension
|
final readonly class UpdateExtension
|
||||||
{
|
{
|
||||||
public function __construct(private readonly UpdateAvailableFacade $updateAvailableManager,
|
public function __construct(private UpdateAvailableFacade $updateAvailableManager,
|
||||||
private readonly Security $security)
|
private Security $security)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
new TwigFunction('is_update_available', $this->isUpdateAvailable(...)),
|
|
||||||
new TwigFunction('get_latest_version', $this->getLatestVersion(...)),
|
|
||||||
new TwigFunction('get_latest_version_url', $this->getLatestVersionUrl(...)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an update is available and the user has permission to see it.
|
* Check if an update is available and the user has permission to see it.
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFunction(name: 'is_update_available')]
|
||||||
public function isUpdateAvailable(): bool
|
public function isUpdateAvailable(): bool
|
||||||
{
|
{
|
||||||
// Only show to users with the show_updates permission
|
// Only show to users with the show_updates permission
|
||||||
|
|
@ -64,6 +57,7 @@ final class UpdateExtension extends AbstractExtension
|
||||||
/**
|
/**
|
||||||
* Get the latest available version string.
|
* Get the latest available version string.
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFunction(name: 'get_latest_version')]
|
||||||
public function getLatestVersion(): string
|
public function getLatestVersion(): string
|
||||||
{
|
{
|
||||||
return $this->updateAvailableManager->getLatestVersionString();
|
return $this->updateAvailableManager->getLatestVersionString();
|
||||||
|
|
@ -72,6 +66,7 @@ final class UpdateExtension extends AbstractExtension
|
||||||
/**
|
/**
|
||||||
* Get the URL to the latest version release page.
|
* Get the URL to the latest version release page.
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFunction(name: 'get_latest_version_url')]
|
||||||
public function getLatestVersionUrl(): string
|
public function getLatestVersionUrl(): string
|
||||||
{
|
{
|
||||||
return $this->updateAvailableManager->getLatestVersionUrl();
|
return $this->updateAvailableManager->getLatestVersionUrl();
|
||||||
|
|
|
||||||
|
|
@ -41,51 +41,24 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use App\Entity\Base\AbstractDBElement;
|
use Twig\Attribute\AsTwigFilter;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Entity\LogSystem\AbstractLogEntry;
|
|
||||||
use App\Repository\LogEntryRepository;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
|
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
|
||||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||||
use Twig\Extension\AbstractExtension;
|
|
||||||
use Twig\TwigFilter;
|
|
||||||
use Twig\TwigFunction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Twig\UserExtensionTest
|
* @see \App\Tests\Twig\UserExtensionTest
|
||||||
*/
|
*/
|
||||||
final class UserExtension extends AbstractExtension
|
final readonly class UserExtension
|
||||||
{
|
{
|
||||||
private readonly LogEntryRepository $repo;
|
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em,
|
public function __construct(
|
||||||
private readonly Security $security,
|
private Security $security,
|
||||||
private readonly UrlGeneratorInterface $urlGenerator)
|
private UrlGeneratorInterface $urlGenerator)
|
||||||
{
|
{
|
||||||
$this->repo = $em->getRepository(AbstractLogEntry::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFilters(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
new TwigFilter('remove_locale_from_path', fn(string $path): string => $this->removeLocaleFromPath($path)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFunctions(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
/* Returns the user which has edited the given entity the last time. */
|
|
||||||
new TwigFunction('last_editing_user', fn(AbstractDBElement $element): ?User => $this->repo->getLastEditingUser($element)),
|
|
||||||
/* Returns the user which has created the given entity. */
|
|
||||||
new TwigFunction('creating_user', fn(AbstractDBElement $element): ?User => $this->repo->getCreatingUser($element)),
|
|
||||||
new TwigFunction('impersonator_user', $this->getImpersonatorUser(...)),
|
|
||||||
new TwigFunction('impersonation_active', $this->isImpersonationActive(...)),
|
|
||||||
new TwigFunction('impersonation_path', $this->getImpersonationPath(...)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,6 +66,7 @@ final class UserExtension extends AbstractExtension
|
||||||
* If the current user is not impersonated, null is returned.
|
* If the current user is not impersonated, null is returned.
|
||||||
* @return User|null
|
* @return User|null
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFunction(name: 'impersonator_user')]
|
||||||
public function getImpersonatorUser(): ?User
|
public function getImpersonatorUser(): ?User
|
||||||
{
|
{
|
||||||
$token = $this->security->getToken();
|
$token = $this->security->getToken();
|
||||||
|
|
@ -107,11 +81,13 @@ final class UserExtension extends AbstractExtension
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFunction(name: 'impersonation_active')]
|
||||||
public function isImpersonationActive(): bool
|
public function isImpersonationActive(): bool
|
||||||
{
|
{
|
||||||
return $this->security->isGranted('IS_IMPERSONATOR');
|
return $this->security->isGranted('IS_IMPERSONATOR');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFunction(name: 'impersonation_path')]
|
||||||
public function getImpersonationPath(User $user, string $route_name = 'homepage'): string
|
public function getImpersonationPath(User $user, string $route_name = 'homepage'): string
|
||||||
{
|
{
|
||||||
if (! $this->security->isGranted('CAN_SWITCH_USER', $user)) {
|
if (! $this->security->isGranted('CAN_SWITCH_USER', $user)) {
|
||||||
|
|
@ -124,6 +100,7 @@ final class UserExtension extends AbstractExtension
|
||||||
/**
|
/**
|
||||||
* This function/filter generates a path.
|
* This function/filter generates a path.
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFilter(name: 'remove_locale_from_path')]
|
||||||
public function removeLocaleFromPath(string $path): string
|
public function removeLocaleFromPath(string $path): string
|
||||||
{
|
{
|
||||||
//Ensure the path has the correct format
|
//Ensure the path has the correct format
|
||||||
|
|
|
||||||
57
src/Twig/UserRepoExtension.php
Normal file
57
src/Twig/UserRepoExtension.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use App\Entity\Base\AbstractDBElement;
|
||||||
|
use App\Entity\LogSystem\AbstractLogEntry;
|
||||||
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Repository\LogEntryRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
|
|
||||||
|
final readonly class UserRepoExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(private EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user which has edited the given entity the last time.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction('creating_user')]
|
||||||
|
public function creatingUser(AbstractDBElement $element): ?User
|
||||||
|
{
|
||||||
|
return $this->entityManager->getRepository(AbstractLogEntry::class)->getCreatingUser($element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user which has edited the given entity the last time.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction('last_editing_user')]
|
||||||
|
public function lastEditingUser(AbstractDBElement $element): ?User
|
||||||
|
{
|
||||||
|
return $this->entityManager->getRepository(AbstractLogEntry::class)->getLastEditingUser($element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Validator\Constraints;
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Validator\Constraints;
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if entity is instanceof("App\\Entity\\Parts\\Storelocation") %}
|
{% if entity is instanceof("App\\Entity\\Parts\\StorageLocation") %}
|
||||||
{{ dropdown.profile_dropdown('storelocation', entity.id, true, 'btn-secondary w-100 mt-2') }}
|
{{ dropdown.profile_dropdown('storelocation', entity.id, true, 'btn-secondary w-100 mt-2') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
@ -136,4 +136,4 @@
|
||||||
{% if filterForm is defined %}
|
{% if filterForm is defined %}
|
||||||
{% include "parts/lists/_filter.html.twig" %}
|
{% include "parts/lists/_filter.html.twig" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use App\Entity\UserSystem\User;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class APIDocsAvailabilityTest extends WebTestCase
|
final class APIDocsAvailabilityTest extends WebTestCase
|
||||||
{
|
{
|
||||||
#[DataProvider('urlProvider')]
|
#[DataProvider('urlProvider')]
|
||||||
public function testDocAvailabilityForLoggedInUser(string $url): void
|
public function testDocAvailabilityForLoggedInUser(string $url): void
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
|
||||||
use App\DataFixtures\APITokenFixtures;
|
use App\DataFixtures\APITokenFixtures;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
use ApiPlatform\Symfony\Bundle\Test\Client;
|
use ApiPlatform\Symfony\Bundle\Test\Client;
|
||||||
class APITokenAuthenticationTest extends ApiTestCase
|
final class APITokenAuthenticationTest extends ApiTestCase
|
||||||
{
|
{
|
||||||
public function testUnauthenticated(): void
|
public function testUnauthenticated(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\AuthenticatedApiTestCase;
|
use App\Tests\API\AuthenticatedApiTestCase;
|
||||||
|
|
||||||
class ApiTokenEnpointTest extends AuthenticatedApiTestCase
|
final class ApiTokenEnpointTest extends AuthenticatedApiTestCase
|
||||||
{
|
{
|
||||||
public function testGetCurrentToken(): void
|
public function testGetCurrentToken(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class AttachmentTypeEndpointTest extends CrudEndpointTestCase
|
final class AttachmentTypeEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\AuthenticatedApiTestCase;
|
use App\Tests\API\AuthenticatedApiTestCase;
|
||||||
|
|
||||||
class AttachmentsEndpointTest extends AuthenticatedApiTestCase
|
final class AttachmentsEndpointTest extends AuthenticatedApiTestCase
|
||||||
{
|
{
|
||||||
public function testGetCollection(): void
|
public function testGetCollection(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class CategoryEndpointTest extends CrudEndpointTestCase
|
final class CategoryEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
|
|
||||||
class CurrencyEndpointTest extends CrudEndpointTestCase
|
final class CurrencyEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class FootprintsEndpointTest extends CrudEndpointTestCase
|
final class FootprintsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\AuthenticatedApiTestCase;
|
use App\Tests\API\AuthenticatedApiTestCase;
|
||||||
|
|
||||||
class InfoEndpointTest extends AuthenticatedApiTestCase
|
final class InfoEndpointTest extends AuthenticatedApiTestCase
|
||||||
{
|
{
|
||||||
public function testGetInfo(): void
|
public function testGetInfo(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class ManufacturersEndpointTest extends CrudEndpointTestCase
|
final class ManufacturersEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class MeasurementUnitsEndpointTest extends CrudEndpointTestCase
|
final class MeasurementUnitsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class OrderdetailsEndpointTest extends CrudEndpointTestCase
|
final class OrderdetailsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class ParametersEndpointTest extends CrudEndpointTestCase
|
final class ParametersEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class PartAssociationsEndpointTest extends CrudEndpointTestCase
|
final class PartAssociationsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class PartCustomStateEndpointTest extends CrudEndpointTestCase
|
final class PartCustomStateEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class PartEndpointTest extends CrudEndpointTestCase
|
final class PartEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class PartLotsEndpointTest extends CrudEndpointTestCase
|
final class PartLotsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class PricedetailsEndpointTest extends CrudEndpointTestCase
|
final class PricedetailsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class ProjectBOMEntriesEndpointTest extends CrudEndpointTestCase
|
final class ProjectBOMEntriesEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class ProjectsEndpointTest extends CrudEndpointTestCase
|
final class ProjectsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class StorageLocationsEndpointTest extends CrudEndpointTestCase
|
final class StorageLocationsEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
use App\Tests\API\Endpoints\CrudEndpointTestCase;
|
||||||
|
|
||||||
class SuppliersEndpointTest extends CrudEndpointTestCase
|
final class SuppliersEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\API\Endpoints;
|
namespace App\Tests\API\Endpoints;
|
||||||
|
|
||||||
class UsersEndpointTest extends CrudEndpointTestCase
|
final class UsersEndpointTest extends CrudEndpointTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function getBasePath(): string
|
protected function getBasePath(): string
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
*/
|
*/
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
class ApplicationAvailabilityFunctionalTest extends WebTestCase
|
final class ApplicationAvailabilityFunctionalTest extends WebTestCase
|
||||||
{
|
{
|
||||||
#[DataProvider('urlProvider')]
|
#[DataProvider('urlProvider')]
|
||||||
public function testPageIsSuccessful(string $url): void
|
public function testPageIsSuccessful(string $url): void
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Attachments\AttachmentType;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class AttachmentTypeController extends AbstractAdminController
|
final class AttachmentTypeController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/attachment_type';
|
protected static string $base_path = '/en/attachment_type';
|
||||||
protected static string $entity_class = AttachmentType::class;
|
protected static string $entity_class = AttachmentType::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\Category;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class CategoryController extends AbstractAdminController
|
final class CategoryController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/category';
|
protected static string $base_path = '/en/category';
|
||||||
protected static string $entity_class = Category::class;
|
protected static string $entity_class = Category::class;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use App\Entity\Parts\Manufacturer;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class CurrencyController extends AbstractAdminController
|
final class CurrencyController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/currency';
|
protected static string $base_path = '/en/currency';
|
||||||
protected static string $entity_class = Currency::class;
|
protected static string $entity_class = Currency::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\Footprint;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class FootprintController extends AbstractAdminController
|
final class FootprintController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/footprint';
|
protected static string $base_path = '/en/footprint';
|
||||||
protected static string $entity_class = Footprint::class;
|
protected static string $entity_class = Footprint::class;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ use PHPUnit\Framework\Attributes\Group;
|
||||||
use App\Entity\LabelSystem\LabelProfile;
|
use App\Entity\LabelSystem\LabelProfile;
|
||||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||||
|
|
||||||
class LabelProfileController extends AbstractAdminController
|
final class LabelProfileController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/label_profile';
|
protected static string $base_path = '/en/label_profile';
|
||||||
protected static string $entity_class = LabelProfile::class;
|
protected static string $entity_class = LabelProfile::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\Manufacturer;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class ManufacturerController extends AbstractAdminController
|
final class ManufacturerController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/manufacturer';
|
protected static string $base_path = '/en/manufacturer';
|
||||||
protected static string $entity_class = Manufacturer::class;
|
protected static string $entity_class = Manufacturer::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\MeasurementUnit;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class MeasurementUnitController extends AbstractAdminController
|
final class MeasurementUnitController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/measurement_unit';
|
protected static string $base_path = '/en/measurement_unit';
|
||||||
protected static string $entity_class = MeasurementUnit::class;
|
protected static string $entity_class = MeasurementUnit::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use PHPUnit\Framework\Attributes\Group;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class PartCustomStateControllerTest extends AbstractAdminController
|
final class PartCustomStateControllerTest extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/part_custom_state';
|
protected static string $base_path = '/en/part_custom_state';
|
||||||
protected static string $entity_class = PartCustomState::class;
|
protected static string $entity_class = PartCustomState::class;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use App\Entity\ProjectSystem\Project;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class ProjectController extends AbstractAdminController
|
final class ProjectController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/project';
|
protected static string $base_path = '/en/project';
|
||||||
protected static string $entity_class = Project::class;
|
protected static string $entity_class = Project::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\StorageLocation;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class StorelocationController extends AbstractAdminController
|
final class StorelocationController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/store_location';
|
protected static string $base_path = '/en/store_location';
|
||||||
protected static string $entity_class = StorageLocation::class;
|
protected static string $entity_class = StorageLocation::class;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\Parts\Supplier;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class SupplierController extends AbstractAdminController
|
final class SupplierController extends AbstractAdminController
|
||||||
{
|
{
|
||||||
protected static string $base_path = '/en/supplier';
|
protected static string $base_path = '/en/supplier';
|
||||||
protected static string $entity_class = Supplier::class;
|
protected static string $entity_class = Supplier::class;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\Controller;
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
|
use App\Services\InfoProviderSystem\BulkInfoProviderService;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO;
|
||||||
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
||||||
use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob;
|
use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
|
|
@ -36,7 +38,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
#[Group("slow")]
|
#[Group("slow")]
|
||||||
#[Group("DB")]
|
#[Group("DB")]
|
||||||
class BulkInfoProviderImportControllerTest extends WebTestCase
|
final class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
public function testStep1WithoutIds(): void
|
public function testStep1WithoutIds(): void
|
||||||
{
|
{
|
||||||
|
|
@ -174,8 +176,8 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
|
|
||||||
// Verify the template rendered the source_field and source_keyword correctly
|
// Verify the template rendered the source_field and source_keyword correctly
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
$this->assertStringContainsString('test_field', $content);
|
$this->assertStringContainsString('test_field', (string) $content);
|
||||||
$this->assertStringContainsString('test_keyword', $content);
|
$this->assertStringContainsString('test_keyword', (string) $content);
|
||||||
|
|
||||||
// Clean up - find by ID to avoid detached entity issues
|
// Clean up - find by ID to avoid detached entity issues
|
||||||
$jobId = $job->getId();
|
$jobId = $job->getId();
|
||||||
|
|
@ -607,7 +609,7 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
|
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
|
||||||
$this->assertStringContainsString('Bulk Info Provider Import', $client->getResponse()->getContent());
|
$this->assertStringContainsString('Bulk Info Provider Import', (string) $client->getResponse()->getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testStep1FormSubmissionWithErrors(): void
|
public function testStep1FormSubmissionWithErrors(): void
|
||||||
|
|
@ -630,7 +632,7 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
|
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
|
||||||
$this->assertStringContainsString('Bulk Info Provider Import', $client->getResponse()->getContent());
|
$this->assertStringContainsString('Bulk Info Provider Import', (string) $client->getResponse()->getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBulkInfoProviderServiceKeywordExtraction(): void
|
public function testBulkInfoProviderServiceKeywordExtraction(): void
|
||||||
|
|
@ -647,18 +649,18 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the service can extract keywords from parts
|
// Test that the service can extract keywords from parts
|
||||||
$bulkService = $client->getContainer()->get(\App\Services\InfoProviderSystem\BulkInfoProviderService::class);
|
$bulkService = $client->getContainer()->get(BulkInfoProviderService::class);
|
||||||
|
|
||||||
// Create field mappings to verify the service works
|
// Create field mappings to verify the service works
|
||||||
$fieldMappings = [
|
$fieldMappings = [
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO('name', ['test'], 1),
|
new BulkSearchFieldMappingDTO('name', ['test'], 1),
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO('mpn', ['test'], 2)
|
new BulkSearchFieldMappingDTO('mpn', ['test'], 2)
|
||||||
];
|
];
|
||||||
|
|
||||||
// The service may return an empty result or throw when no results are found
|
// The service may return an empty result or throw when no results are found
|
||||||
try {
|
try {
|
||||||
$result = $bulkService->performBulkSearch([$part], $fieldMappings, false);
|
$result = $bulkService->performBulkSearch([$part], $fieldMappings, false);
|
||||||
$this->assertInstanceOf(\App\Services\InfoProviderSystem\DTOs\BulkSearchResponseDTO::class, $result);
|
$this->assertInstanceOf(BulkSearchResponseDTO::class, $result);
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
$this->assertStringContainsString('No search results found', $e->getMessage());
|
$this->assertStringContainsString('No search results found', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -725,12 +727,12 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the service can handle supplier part number fields
|
// Test that the service can handle supplier part number fields
|
||||||
$bulkService = $client->getContainer()->get(\App\Services\InfoProviderSystem\BulkInfoProviderService::class);
|
$bulkService = $client->getContainer()->get(BulkInfoProviderService::class);
|
||||||
|
|
||||||
// Create field mappings with supplier SPN field mapping
|
// Create field mappings with supplier SPN field mapping
|
||||||
$fieldMappings = [
|
$fieldMappings = [
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO('invalid_field', ['test'], 1),
|
new BulkSearchFieldMappingDTO('invalid_field', ['test'], 1),
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO('test_supplier_spn', ['test'], 2)
|
new BulkSearchFieldMappingDTO('test_supplier_spn', ['test'], 2)
|
||||||
];
|
];
|
||||||
|
|
||||||
// The service should be able to process the request and throw an exception when no results are found
|
// The service should be able to process the request and throw an exception when no results are found
|
||||||
|
|
@ -756,11 +758,11 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the service can handle batch processing
|
// Test that the service can handle batch processing
|
||||||
$bulkService = $client->getContainer()->get(\App\Services\InfoProviderSystem\BulkInfoProviderService::class);
|
$bulkService = $client->getContainer()->get(BulkInfoProviderService::class);
|
||||||
|
|
||||||
// Create field mappings with multiple keywords
|
// Create field mappings with multiple keywords
|
||||||
$fieldMappings = [
|
$fieldMappings = [
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchFieldMappingDTO('empty', ['test'], 1)
|
new BulkSearchFieldMappingDTO('empty', ['test'], 1)
|
||||||
];
|
];
|
||||||
|
|
||||||
// The service should be able to process the request and throw an exception when no results are found
|
// The service should be able to process the request and throw an exception when no results are found
|
||||||
|
|
@ -786,7 +788,7 @@ class BulkInfoProviderImportControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the service can handle prefetch details
|
// Test that the service can handle prefetch details
|
||||||
$bulkService = $client->getContainer()->get(\App\Services\InfoProviderSystem\BulkInfoProviderService::class);
|
$bulkService = $client->getContainer()->get(BulkInfoProviderService::class);
|
||||||
|
|
||||||
// Create empty search results to test prefetch method
|
// Create empty search results to test prefetch method
|
||||||
$searchResults = new BulkSearchResponseDTO([
|
$searchResults = new BulkSearchResponseDTO([
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\DataFixtures\APITokenFixtures;
|
||||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class KiCadApiControllerTest extends WebTestCase
|
final class KiCadApiControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
private const BASE_URL = '/en/kicad-api/v1';
|
private const BASE_URL = '/en/kicad-api/v1';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
#[Group("slow")]
|
#[Group("slow")]
|
||||||
#[Group("DB")]
|
#[Group("DB")]
|
||||||
class PartControllerTest extends WebTestCase
|
final class PartControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
public function testShowPart(): void
|
public function testShowPart(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
#[Group('slow')]
|
#[Group('slow')]
|
||||||
#[Group('DB')]
|
#[Group('DB')]
|
||||||
class RedirectControllerTest extends WebTestCase
|
final class RedirectControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
protected EntityManagerInterface $em;
|
protected EntityManagerInterface $em;
|
||||||
protected UserRepository $userRepo;
|
protected UserRepository $userRepo;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\Controller;
|
||||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class ScanControllerTest extends WebTestCase
|
final class ScanControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
private ?KernelBrowser $client = null;
|
private ?KernelBrowser $client = null;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\DataTables\Filters\FilterInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class CompoundFilterTraitTest extends TestCase
|
final class CompoundFilterTraitTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testFindAllChildFiltersEmpty(): void
|
public function testFindAllChildFiltersEmpty(): void
|
||||||
|
|
@ -49,9 +49,9 @@ class CompoundFilterTraitTest extends TestCase
|
||||||
|
|
||||||
public function testFindAllChildFilters(): void
|
public function testFindAllChildFilters(): void
|
||||||
{
|
{
|
||||||
$f1 = $this->createMock(FilterInterface::class);
|
$f1 = $this->createStub(FilterInterface::class);
|
||||||
$f2 = $this->createMock(FilterInterface::class);
|
$f2 = $this->createStub(FilterInterface::class);
|
||||||
$f3 = $this->createMock(FilterInterface::class);
|
$f3 = $this->createStub(FilterInterface::class);
|
||||||
|
|
||||||
$filter = new class($f1, $f2, $f3, null) {
|
$filter = new class($f1, $f2, $f3, null) {
|
||||||
use CompoundFilterTrait;
|
use CompoundFilterTrait;
|
||||||
|
|
@ -108,7 +108,7 @@ class CompoundFilterTraitTest extends TestCase
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$qb = $this->createMock(QueryBuilder::class);
|
$qb = $this->createStub(QueryBuilder::class);
|
||||||
$filter->_applyAllChildFilters($qb);
|
$filter->_applyAllChildFilters($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use App\DataTables\Filters\Constraints\FilterTrait;
|
use App\DataTables\Filters\Constraints\FilterTrait;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class FilterTraitTest extends TestCase
|
final class FilterTraitTest extends TestCase
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BulkImportJobStatusConstraintTest extends TestCase
|
final class BulkImportJobStatusConstraintTest extends TestCase
|
||||||
{
|
{
|
||||||
private BulkImportJobStatusConstraint $constraint;
|
private BulkImportJobStatusConstraint $constraint;
|
||||||
private QueryBuilder $queryBuilder;
|
private QueryBuilder $queryBuilder;
|
||||||
|
|
@ -46,7 +46,7 @@ class BulkImportJobStatusConstraintTest extends TestCase
|
||||||
|
|
||||||
public function testConstructor(): void
|
public function testConstructor(): void
|
||||||
{
|
{
|
||||||
$this->assertEquals([], $this->constraint->getValue());
|
$this->assertSame([], $this->constraint->getValue());
|
||||||
$this->assertEmpty($this->constraint->getOperator());
|
$this->assertEmpty($this->constraint->getOperator());
|
||||||
$this->assertFalse($this->constraint->isEnabled());
|
$this->assertFalse($this->constraint->isEnabled());
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ class BulkImportJobStatusConstraintTest extends TestCase
|
||||||
$values = ['pending', 'in_progress'];
|
$values = ['pending', 'in_progress'];
|
||||||
$this->constraint->setValue($values);
|
$this->constraint->setValue($values);
|
||||||
|
|
||||||
$this->assertEquals($values, $this->constraint->getValue());
|
$this->assertSame($values, $this->constraint->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAndSetOperator(): void
|
public function testGetAndSetOperator(): void
|
||||||
|
|
@ -64,7 +64,7 @@ class BulkImportJobStatusConstraintTest extends TestCase
|
||||||
$operator = 'ANY';
|
$operator = 'ANY';
|
||||||
$this->constraint->setOperator($operator);
|
$this->constraint->setOperator($operator);
|
||||||
|
|
||||||
$this->assertEquals($operator, $this->constraint->getOperator());
|
$this->assertSame($operator, $this->constraint->getOperator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsEnabledWithEmptyValues(): void
|
public function testIsEnabledWithEmptyValues(): void
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BulkImportPartStatusConstraintTest extends TestCase
|
final class BulkImportPartStatusConstraintTest extends TestCase
|
||||||
{
|
{
|
||||||
private BulkImportPartStatusConstraint $constraint;
|
private BulkImportPartStatusConstraint $constraint;
|
||||||
private QueryBuilder $queryBuilder;
|
private QueryBuilder $queryBuilder;
|
||||||
|
|
@ -46,7 +46,7 @@ class BulkImportPartStatusConstraintTest extends TestCase
|
||||||
|
|
||||||
public function testConstructor(): void
|
public function testConstructor(): void
|
||||||
{
|
{
|
||||||
$this->assertEquals([], $this->constraint->getValue());
|
$this->assertSame([], $this->constraint->getValue());
|
||||||
$this->assertEmpty($this->constraint->getOperator());
|
$this->assertEmpty($this->constraint->getOperator());
|
||||||
$this->assertFalse($this->constraint->isEnabled());
|
$this->assertFalse($this->constraint->isEnabled());
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ class BulkImportPartStatusConstraintTest extends TestCase
|
||||||
$values = ['pending', 'completed', 'skipped'];
|
$values = ['pending', 'completed', 'skipped'];
|
||||||
$this->constraint->setValue($values);
|
$this->constraint->setValue($values);
|
||||||
|
|
||||||
$this->assertEquals($values, $this->constraint->getValue());
|
$this->assertSame($values, $this->constraint->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAndSetOperator(): void
|
public function testGetAndSetOperator(): void
|
||||||
|
|
@ -64,7 +64,7 @@ class BulkImportPartStatusConstraintTest extends TestCase
|
||||||
$operator = 'ANY';
|
$operator = 'ANY';
|
||||||
$this->constraint->setOperator($operator);
|
$this->constraint->setOperator($operator);
|
||||||
|
|
||||||
$this->assertEquals($operator, $this->constraint->getOperator());
|
$this->assertSame($operator, $this->constraint->getOperator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsEnabledWithEmptyValues(): void
|
public function testIsEnabledWithEmptyValues(): void
|
||||||
|
|
@ -294,6 +294,6 @@ class BulkImportPartStatusConstraintTest extends TestCase
|
||||||
|
|
||||||
$this->constraint->apply($this->queryBuilder);
|
$this->constraint->apply($this->queryBuilder);
|
||||||
|
|
||||||
$this->assertEquals($statusValues, $this->constraint->getValue());
|
$this->assertSame($statusValues, $this->constraint->getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace App\Tests;
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class DatatablesAvailabilityTest extends WebTestCase
|
final class DatatablesAvailabilityTest extends WebTestCase
|
||||||
{
|
{
|
||||||
#[DataProvider('urlProvider')]
|
#[DataProvider('urlProvider')]
|
||||||
public function testDataTable(string $url, ?array $ordering = null): void
|
public function testDataTable(string $url, ?array $ordering = null): void
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use App\Doctrine\Middleware\SQLiteRegexExtensionMiddlewareDriver;
|
use App\Doctrine\Middleware\SQLiteRegexExtensionMiddlewareDriver;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class SQLiteRegexMiddlewareTest extends TestCase
|
final class SQLiteRegexMiddlewareTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function regexpDataProvider(): \Generator
|
public static function regexpDataProvider(): \Generator
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ use InvalidArgumentException;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
class AttachmentTest extends TestCase
|
final class AttachmentTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testEmptyState(): void
|
public function testEmptyState(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use App\Entity\Attachments\UserAttachment;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class AttachmentTypeTest extends TestCase
|
final class AttachmentTypeTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testEmptyState(): void
|
public function testEmptyState(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ use PHPUnit\Framework\TestCase;
|
||||||
* Test StructuralDBElement entities.
|
* Test StructuralDBElement entities.
|
||||||
* Note: Because StructuralDBElement is abstract we use AttachmentType here as a placeholder.
|
* Note: Because StructuralDBElement is abstract we use AttachmentType here as a placeholder.
|
||||||
*/
|
*/
|
||||||
class AbstractStructuralDBElementTest extends TestCase
|
final class AbstractStructuralDBElementTest extends TestCase
|
||||||
{
|
{
|
||||||
protected AttachmentType $root;
|
protected AttachmentType $root;
|
||||||
protected AttachmentType $child1;
|
protected AttachmentType $child1;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\Entity;
|
||||||
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BulkImportJobStatusTest extends TestCase
|
final class BulkImportJobStatusTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testEnumValues(): void
|
public function testEnumValues(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,32 +28,25 @@ use App\Entity\InfoProviderSystem\BulkInfoProviderImportJobPart;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BulkInfoProviderImportJobPartTest extends TestCase
|
final class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
{
|
{
|
||||||
private BulkInfoProviderImportJob $job;
|
|
||||||
private Part $part;
|
|
||||||
private BulkInfoProviderImportJobPart $jobPart;
|
private BulkInfoProviderImportJobPart $jobPart;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->job = $this->createMock(BulkInfoProviderImportJob::class);
|
$this->jobPart = new BulkInfoProviderImportJobPart($this->createStub(BulkInfoProviderImportJob::class), $this->createStub(Part::class));
|
||||||
$this->part = $this->createMock(Part::class);
|
|
||||||
|
|
||||||
$this->jobPart = new BulkInfoProviderImportJobPart($this->job, $this->part);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConstructor(): void
|
public function testConstructor(): void
|
||||||
{
|
{
|
||||||
$this->assertSame($this->job, $this->jobPart->getJob());
|
$this->assertSame(BulkImportPartStatus::PENDING, $this->jobPart->getStatus());
|
||||||
$this->assertSame($this->part, $this->jobPart->getPart());
|
|
||||||
$this->assertEquals(BulkImportPartStatus::PENDING, $this->jobPart->getStatus());
|
|
||||||
$this->assertNull($this->jobPart->getReason());
|
$this->assertNull($this->jobPart->getReason());
|
||||||
$this->assertNull($this->jobPart->getCompletedAt());
|
$this->assertNull($this->jobPart->getCompletedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAndSetJob(): void
|
public function testGetAndSetJob(): void
|
||||||
{
|
{
|
||||||
$newJob = $this->createMock(BulkInfoProviderImportJob::class);
|
$newJob = $this->createStub(BulkInfoProviderImportJob::class);
|
||||||
|
|
||||||
$result = $this->jobPart->setJob($newJob);
|
$result = $this->jobPart->setJob($newJob);
|
||||||
|
|
||||||
|
|
@ -63,7 +56,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
|
|
||||||
public function testGetAndSetPart(): void
|
public function testGetAndSetPart(): void
|
||||||
{
|
{
|
||||||
$newPart = $this->createMock(Part::class);
|
$newPart = $this->createStub(Part::class);
|
||||||
|
|
||||||
$result = $this->jobPart->setPart($newPart);
|
$result = $this->jobPart->setPart($newPart);
|
||||||
|
|
||||||
|
|
@ -76,7 +69,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$result = $this->jobPart->setStatus(BulkImportPartStatus::COMPLETED);
|
$result = $this->jobPart->setStatus(BulkImportPartStatus::COMPLETED);
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::COMPLETED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::COMPLETED, $this->jobPart->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAndSetReason(): void
|
public function testGetAndSetReason(): void
|
||||||
|
|
@ -86,7 +79,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$result = $this->jobPart->setReason($reason);
|
$result = $this->jobPart->setReason($reason);
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals($reason, $this->jobPart->getReason());
|
$this->assertSame($reason, $this->jobPart->getReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAndSetCompletedAt(): void
|
public function testGetAndSetCompletedAt(): void
|
||||||
|
|
@ -108,7 +101,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$afterTime = new \DateTimeImmutable();
|
$afterTime = new \DateTimeImmutable();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::COMPLETED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::COMPLETED, $this->jobPart->getStatus());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
||||||
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
||||||
|
|
@ -124,8 +117,8 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$afterTime = new \DateTimeImmutable();
|
$afterTime = new \DateTimeImmutable();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::SKIPPED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::SKIPPED, $this->jobPart->getStatus());
|
||||||
$this->assertEquals($reason, $this->jobPart->getReason());
|
$this->assertSame($reason, $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
||||||
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
||||||
|
|
@ -136,8 +129,8 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$result = $this->jobPart->markAsSkipped();
|
$result = $this->jobPart->markAsSkipped();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::SKIPPED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::SKIPPED, $this->jobPart->getStatus());
|
||||||
$this->assertEquals('', $this->jobPart->getReason());
|
$this->assertSame('', $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,8 +144,8 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$afterTime = new \DateTimeImmutable();
|
$afterTime = new \DateTimeImmutable();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::FAILED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::FAILED, $this->jobPart->getStatus());
|
||||||
$this->assertEquals($reason, $this->jobPart->getReason());
|
$this->assertSame($reason, $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
$this->assertGreaterThanOrEqual($beforeTime, $this->jobPart->getCompletedAt());
|
||||||
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
$this->assertLessThanOrEqual($afterTime, $this->jobPart->getCompletedAt());
|
||||||
|
|
@ -163,8 +156,8 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$result = $this->jobPart->markAsFailed();
|
$result = $this->jobPart->markAsFailed();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::FAILED, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::FAILED, $this->jobPart->getStatus());
|
||||||
$this->assertEquals('', $this->jobPart->getReason());
|
$this->assertSame('', $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +169,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
$result = $this->jobPart->markAsPending();
|
$result = $this->jobPart->markAsPending();
|
||||||
|
|
||||||
$this->assertSame($this->jobPart, $result);
|
$this->assertSame($this->jobPart, $result);
|
||||||
$this->assertEquals(BulkImportPartStatus::PENDING, $this->jobPart->getStatus());
|
$this->assertSame(BulkImportPartStatus::PENDING, $this->jobPart->getStatus());
|
||||||
$this->assertNull($this->jobPart->getReason());
|
$this->assertNull($this->jobPart->getReason());
|
||||||
$this->assertNull($this->jobPart->getCompletedAt());
|
$this->assertNull($this->jobPart->getCompletedAt());
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +274,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
|
|
||||||
// After marking as skipped, should have reason and completion time
|
// After marking as skipped, should have reason and completion time
|
||||||
$this->jobPart->markAsSkipped('Skipped reason');
|
$this->jobPart->markAsSkipped('Skipped reason');
|
||||||
$this->assertEquals('Skipped reason', $this->jobPart->getReason());
|
$this->assertSame('Skipped reason', $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
|
|
||||||
// After marking as pending, reason and completion time should be cleared
|
// After marking as pending, reason and completion time should be cleared
|
||||||
|
|
@ -291,7 +284,7 @@ class BulkInfoProviderImportJobPartTest extends TestCase
|
||||||
|
|
||||||
// After marking as failed, should have reason and completion time
|
// After marking as failed, should have reason and completion time
|
||||||
$this->jobPart->markAsFailed('Failed reason');
|
$this->jobPart->markAsFailed('Failed reason');
|
||||||
$this->assertEquals('Failed reason', $this->jobPart->getReason());
|
$this->assertSame('Failed reason', $this->jobPart->getReason());
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $this->jobPart->getCompletedAt());
|
||||||
|
|
||||||
// After marking as completed, should have completion time (reason may remain from previous state)
|
// After marking as completed, should have completion time (reason may remain from previous state)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\Entity;
|
namespace App\Tests\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Parts\Part;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\BulkSearchPartResultsDTO;
|
||||||
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
use App\Entity\InfoProviderSystem\BulkImportJobStatus;
|
||||||
use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob;
|
use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
|
@ -31,7 +33,7 @@ use App\Services\InfoProviderSystem\DTOs\BulkSearchResponseDTO;
|
||||||
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BulkInfoProviderImportJobTest extends TestCase
|
final class BulkInfoProviderImportJobTest extends TestCase
|
||||||
{
|
{
|
||||||
private BulkInfoProviderImportJob $job;
|
private BulkInfoProviderImportJob $job;
|
||||||
private User $user;
|
private User $user;
|
||||||
|
|
@ -45,9 +47,9 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$this->job->setCreatedBy($this->user);
|
$this->job->setCreatedBy($this->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createMockPart(int $id): \App\Entity\Parts\Part
|
private function createMockPart(int $id): Part
|
||||||
{
|
{
|
||||||
$part = $this->createMock(\App\Entity\Parts\Part::class);
|
$part = $this->createMock(Part::class);
|
||||||
$part->method('getId')->willReturn($id);
|
$part->method('getId')->willReturn($id);
|
||||||
$part->method('getName')->willReturn("Test Part {$id}");
|
$part->method('getName')->willReturn("Test Part {$id}");
|
||||||
return $part;
|
return $part;
|
||||||
|
|
@ -58,7 +60,7 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$job = new BulkInfoProviderImportJob();
|
$job = new BulkInfoProviderImportJob();
|
||||||
|
|
||||||
$this->assertInstanceOf(\DateTimeImmutable::class, $job->getCreatedAt());
|
$this->assertInstanceOf(\DateTimeImmutable::class, $job->getCreatedAt());
|
||||||
$this->assertEquals(BulkImportJobStatus::PENDING, $job->getStatus());
|
$this->assertSame(BulkImportJobStatus::PENDING, $job->getStatus());
|
||||||
$this->assertEmpty($job->getPartIds());
|
$this->assertEmpty($job->getPartIds());
|
||||||
$this->assertEmpty($job->getFieldMappings());
|
$this->assertEmpty($job->getFieldMappings());
|
||||||
$this->assertEmpty($job->getSearchResultsRaw());
|
$this->assertEmpty($job->getSearchResultsRaw());
|
||||||
|
|
@ -70,14 +72,14 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
public function testBasicGettersSetters(): void
|
public function testBasicGettersSetters(): void
|
||||||
{
|
{
|
||||||
$this->job->setName('Test Job');
|
$this->job->setName('Test Job');
|
||||||
$this->assertEquals('Test Job', $this->job->getName());
|
$this->assertSame('Test Job', $this->job->getName());
|
||||||
|
|
||||||
// Test with actual parts - this is what actually works
|
// Test with actual parts - this is what actually works
|
||||||
$parts = [$this->createMockPart(1), $this->createMockPart(2), $this->createMockPart(3)];
|
$parts = [$this->createMockPart(1), $this->createMockPart(2), $this->createMockPart(3)];
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
$this->job->addPart($part);
|
$this->job->addPart($part);
|
||||||
}
|
}
|
||||||
$this->assertEquals([1, 2, 3], $this->job->getPartIds());
|
$this->assertSame([1, 2, 3], $this->job->getPartIds());
|
||||||
|
|
||||||
$fieldMappings = [new BulkSearchFieldMappingDTO(field: 'field1', providers: ['provider1', 'provider2'])];
|
$fieldMappings = [new BulkSearchFieldMappingDTO(field: 'field1', providers: ['provider1', 'provider2'])];
|
||||||
$this->job->setFieldMappings($fieldMappings);
|
$this->job->setFieldMappings($fieldMappings);
|
||||||
|
|
@ -98,24 +100,24 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$this->assertFalse($this->job->isStopped());
|
$this->assertFalse($this->job->isStopped());
|
||||||
|
|
||||||
$this->job->markAsInProgress();
|
$this->job->markAsInProgress();
|
||||||
$this->assertEquals(BulkImportJobStatus::IN_PROGRESS, $this->job->getStatus());
|
$this->assertSame(BulkImportJobStatus::IN_PROGRESS, $this->job->getStatus());
|
||||||
$this->assertTrue($this->job->isInProgress());
|
$this->assertTrue($this->job->isInProgress());
|
||||||
$this->assertFalse($this->job->isPending());
|
$this->assertFalse($this->job->isPending());
|
||||||
|
|
||||||
$this->job->markAsCompleted();
|
$this->job->markAsCompleted();
|
||||||
$this->assertEquals(BulkImportJobStatus::COMPLETED, $this->job->getStatus());
|
$this->assertSame(BulkImportJobStatus::COMPLETED, $this->job->getStatus());
|
||||||
$this->assertTrue($this->job->isCompleted());
|
$this->assertTrue($this->job->isCompleted());
|
||||||
$this->assertNotNull($this->job->getCompletedAt());
|
$this->assertNotNull($this->job->getCompletedAt());
|
||||||
|
|
||||||
$job2 = new BulkInfoProviderImportJob();
|
$job2 = new BulkInfoProviderImportJob();
|
||||||
$job2->markAsFailed();
|
$job2->markAsFailed();
|
||||||
$this->assertEquals(BulkImportJobStatus::FAILED, $job2->getStatus());
|
$this->assertSame(BulkImportJobStatus::FAILED, $job2->getStatus());
|
||||||
$this->assertTrue($job2->isFailed());
|
$this->assertTrue($job2->isFailed());
|
||||||
$this->assertNotNull($job2->getCompletedAt());
|
$this->assertNotNull($job2->getCompletedAt());
|
||||||
|
|
||||||
$job3 = new BulkInfoProviderImportJob();
|
$job3 = new BulkInfoProviderImportJob();
|
||||||
$job3->markAsStopped();
|
$job3->markAsStopped();
|
||||||
$this->assertEquals(BulkImportJobStatus::STOPPED, $job3->getStatus());
|
$this->assertSame(BulkImportJobStatus::STOPPED, $job3->getStatus());
|
||||||
$this->assertTrue($job3->isStopped());
|
$this->assertTrue($job3->isStopped());
|
||||||
$this->assertNotNull($job3->getCompletedAt());
|
$this->assertNotNull($job3->getCompletedAt());
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +141,7 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
|
|
||||||
public function testPartCount(): void
|
public function testPartCount(): void
|
||||||
{
|
{
|
||||||
$this->assertEquals(0, $this->job->getPartCount());
|
$this->assertSame(0, $this->job->getPartCount());
|
||||||
|
|
||||||
// Test with actual parts - setPartIds doesn't actually add parts
|
// Test with actual parts - setPartIds doesn't actually add parts
|
||||||
$parts = [
|
$parts = [
|
||||||
|
|
@ -152,31 +154,31 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
$this->job->addPart($part);
|
$this->job->addPart($part);
|
||||||
}
|
}
|
||||||
$this->assertEquals(5, $this->job->getPartCount());
|
$this->assertSame(5, $this->job->getPartCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testResultCount(): void
|
public function testResultCount(): void
|
||||||
{
|
{
|
||||||
$this->assertEquals(0, $this->job->getResultCount());
|
$this->assertSame(0, $this->job->getResultCount());
|
||||||
|
|
||||||
$searchResults = new BulkSearchResponseDTO([
|
$searchResults = new BulkSearchResponseDTO([
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchPartResultsDTO(
|
new BulkSearchPartResultsDTO(
|
||||||
part: $this->createMockPart(1),
|
part: $this->createMockPart(1),
|
||||||
searchResults: [new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '1234', name: 'Part 1', description: 'A part'))]
|
searchResults: [new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '1234', name: 'Part 1', description: 'A part'))]
|
||||||
),
|
),
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchPartResultsDTO(
|
new BulkSearchPartResultsDTO(
|
||||||
part: $this->createMockPart(2),
|
part: $this->createMockPart(2),
|
||||||
searchResults: [new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '1234', name: 'Part 2', description: 'A part')),
|
searchResults: [new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '1234', name: 'Part 2', description: 'A part')),
|
||||||
new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '5678', name: 'Part 2 Alt', description: 'Another part'))]
|
new BulkSearchPartResultDTO(searchResult: new SearchResultDTO(provider_key: 'dummy', provider_id: '5678', name: 'Part 2 Alt', description: 'Another part'))]
|
||||||
),
|
),
|
||||||
new \App\Services\InfoProviderSystem\DTOs\BulkSearchPartResultsDTO(
|
new BulkSearchPartResultsDTO(
|
||||||
part: $this->createMockPart(3),
|
part: $this->createMockPart(3),
|
||||||
searchResults: []
|
searchResults: []
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->job->setSearchResults($searchResults);
|
$this->job->setSearchResults($searchResults);
|
||||||
$this->assertEquals(3, $this->job->getResultCount());
|
$this->assertSame(3, $this->job->getResultCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPartProgressTracking(): void
|
public function testPartProgressTracking(): void
|
||||||
|
|
@ -222,21 +224,21 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$this->job->addPart($part);
|
$this->job->addPart($part);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertEquals(0, $this->job->getCompletedPartsCount());
|
$this->assertSame(0, $this->job->getCompletedPartsCount());
|
||||||
$this->assertEquals(0, $this->job->getSkippedPartsCount());
|
$this->assertSame(0, $this->job->getSkippedPartsCount());
|
||||||
|
|
||||||
$this->job->markPartAsCompleted(1);
|
$this->job->markPartAsCompleted(1);
|
||||||
$this->job->markPartAsCompleted(2);
|
$this->job->markPartAsCompleted(2);
|
||||||
$this->job->markPartAsSkipped(3, 'Error');
|
$this->job->markPartAsSkipped(3, 'Error');
|
||||||
|
|
||||||
$this->assertEquals(2, $this->job->getCompletedPartsCount());
|
$this->assertSame(2, $this->job->getCompletedPartsCount());
|
||||||
$this->assertEquals(1, $this->job->getSkippedPartsCount());
|
$this->assertSame(1, $this->job->getSkippedPartsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testProgressPercentage(): void
|
public function testProgressPercentage(): void
|
||||||
{
|
{
|
||||||
$emptyJob = new BulkInfoProviderImportJob();
|
$emptyJob = new BulkInfoProviderImportJob();
|
||||||
$this->assertEquals(100.0, $emptyJob->getProgressPercentage());
|
$this->assertEqualsWithDelta(100.0, $emptyJob->getProgressPercentage(), PHP_FLOAT_EPSILON);
|
||||||
|
|
||||||
// Test with actual parts - setPartIds doesn't actually add parts
|
// Test with actual parts - setPartIds doesn't actually add parts
|
||||||
$parts = [
|
$parts = [
|
||||||
|
|
@ -250,18 +252,18 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$this->job->addPart($part);
|
$this->job->addPart($part);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertEquals(0.0, $this->job->getProgressPercentage());
|
$this->assertEqualsWithDelta(0.0, $this->job->getProgressPercentage(), PHP_FLOAT_EPSILON);
|
||||||
|
|
||||||
$this->job->markPartAsCompleted(1);
|
$this->job->markPartAsCompleted(1);
|
||||||
$this->job->markPartAsCompleted(2);
|
$this->job->markPartAsCompleted(2);
|
||||||
$this->assertEquals(40.0, $this->job->getProgressPercentage());
|
$this->assertEqualsWithDelta(40.0, $this->job->getProgressPercentage(), PHP_FLOAT_EPSILON);
|
||||||
|
|
||||||
$this->job->markPartAsSkipped(3, 'Error');
|
$this->job->markPartAsSkipped(3, 'Error');
|
||||||
$this->assertEquals(60.0, $this->job->getProgressPercentage());
|
$this->assertEqualsWithDelta(60.0, $this->job->getProgressPercentage(), PHP_FLOAT_EPSILON);
|
||||||
|
|
||||||
$this->job->markPartAsCompleted(4);
|
$this->job->markPartAsCompleted(4);
|
||||||
$this->job->markPartAsCompleted(5);
|
$this->job->markPartAsCompleted(5);
|
||||||
$this->assertEquals(100.0, $this->job->getProgressPercentage());
|
$this->assertEqualsWithDelta(100.0, $this->job->getProgressPercentage(), PHP_FLOAT_EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsAllPartsCompleted(): void
|
public function testIsAllPartsCompleted(): void
|
||||||
|
|
@ -301,8 +303,8 @@ class BulkInfoProviderImportJobTest extends TestCase
|
||||||
$this->job->addPart($part);
|
$this->job->addPart($part);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertEquals('info_providers.bulk_import.job_name_template', $this->job->getDisplayNameKey());
|
$this->assertSame('info_providers.bulk_import.job_name_template', $this->job->getDisplayNameKey());
|
||||||
$this->assertEquals(['%count%' => 3], $this->job->getDisplayNameParams());
|
$this->assertSame(['%count%' => 3], $this->job->getDisplayNameParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFormattedTimestamp(): void
|
public function testFormattedTimestamp(): void
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ use App\Entity\UserSystem\Group;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class AbstractLogEntryTest extends TestCase
|
final class AbstractLogEntryTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testSetGetTarget(): void
|
public function testSetGetTarget(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\Entity\LogSystem;
|
||||||
use App\Entity\LogSystem\LogLevel;
|
use App\Entity\LogSystem\LogLevel;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class LogLevelTest extends TestCase
|
final class LogLevelTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testToPSR3LevelString(): void
|
public function testToPSR3LevelString(): void
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ use App\Entity\Parts\Category;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class LogTargetTypeTest extends TestCase
|
final class LogTargetTypeTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testToClass(): void
|
public function testToClass(): void
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use App\Entity\Parameters\PartParameter;
|
use App\Entity\Parameters\PartParameter;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PartParameterTest extends TestCase
|
final class PartParameterTest extends TestCase
|
||||||
{
|
{
|
||||||
public static function valueWithUnitDataProvider(): \Iterator
|
public static function valueWithUnitDataProvider(): \Iterator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use App\Entity\Parts\InfoProviderReference;
|
||||||
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class InfoProviderReferenceTest extends TestCase
|
final class InfoProviderReferenceTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testNoProvider(): void
|
public function testNoProvider(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use App\Entity\Parts\AssociationType;
|
||||||
use App\Entity\Parts\PartAssociation;
|
use App\Entity\Parts\PartAssociation;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PartAssociationTest extends TestCase
|
final class PartAssociationTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testGetTypeTranslationKey(): void
|
public function testGetTypeTranslationKey(): void
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use App\Entity\Parts\PartLot;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PartLotTest extends TestCase
|
final class PartLotTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testIsExpired(): void
|
public function testIsExpired(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ use DateTime;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PartTest extends TestCase
|
final class PartTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testAddRemovePartLot(): void
|
public function testAddRemovePartLot(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use App\Entity\PriceInformations\Currency;
|
||||||
use Brick\Math\BigDecimal;
|
use Brick\Math\BigDecimal;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class CurrencyTest extends TestCase
|
final class CurrencyTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetInverseExchangeRate(): void
|
public function testGetInverseExchangeRate(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use App\Entity\PriceInformations\Pricedetail;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class OrderdetailTest extends TestCase
|
final class OrderdetailTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testAddRemovePricdetails(): void
|
public function testAddRemovePricdetails(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use App\Entity\PriceInformations\Pricedetail;
|
||||||
use Brick\Math\BigDecimal;
|
use Brick\Math\BigDecimal;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PricedetailTest extends TestCase
|
final class PricedetailTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetPricePerUnit(): void
|
public function testGetPricePerUnit(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\Entity\UserSystem;
|
||||||
use App\Entity\UserSystem\ApiTokenType;
|
use App\Entity\UserSystem\ApiTokenType;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ApiTokenTypeTest extends TestCase
|
final class ApiTokenTypeTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testGetTokenPrefix(): void
|
public function testGetTokenPrefix(): void
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace App\Tests\Entity\UserSystem;
|
||||||
use App\Entity\UserSystem\PermissionData;
|
use App\Entity\UserSystem\PermissionData;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PermissionDataTest extends TestCase
|
final class PermissionDataTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testGetSetIs(): void
|
public function testGetSetIs(): void
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Uid\Uuid;
|
use Symfony\Component\Uid\Uuid;
|
||||||
use Webauthn\TrustPath\EmptyTrustPath;
|
use Webauthn\TrustPath\EmptyTrustPath;
|
||||||
|
|
||||||
class UserTest extends TestCase
|
final class UserTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetFullName(): void
|
public function testGetFullName(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
*
|
*
|
||||||
|
|
@ -17,13 +20,12 @@
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Tests\EnvVarProcessors;
|
namespace App\Tests\EnvVarProcessors;
|
||||||
|
|
||||||
use App\EnvVarProcessors\AddSlashEnvVarProcessor;
|
use App\EnvVarProcessors\AddSlashEnvVarProcessor;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class AddSlashEnvVarProcessorTest extends TestCase
|
final class AddSlashEnvVarProcessorTest extends TestCase
|
||||||
{
|
{
|
||||||
protected AddSlashEnvVarProcessor $processor;
|
protected AddSlashEnvVarProcessor $processor;
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue