From f2816803daa25ba294f1591068cab1d309c6bc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ig=C3=B3n?= Date: Thu, 6 Mar 2025 12:53:32 +0100 Subject: [PATCH 1/9] add entrypoint to dockerfile --- docker/php/Dockerfile | 5 +++++ entrypoint.sh | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 entrypoint.sh diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 9219990..0233faa 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -54,4 +54,9 @@ RUN composer install --prefer-dist --no-scripts --no-dev --optimize-autoloader EXPOSE 9000 +COPY entrypoint.sh /usr/bin +RUN chmod +x /usr/bin/entrypoint.sh + +ENTRYPOINT ["/usr/bin/entrypoint.sh"] + CMD ["php-fpm"] diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..f9e1a07 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +echo "Creating the database if it does not exist..." +php bin/console doctrine:database:create --if-not-exists + +# Check if migrations need to be applied +if ! php bin/console doctrine:migrations:up-to-date --quiet; then + echo "Applying migrations..." + php bin/console doctrine:migrations:migrate --no-interaction +fi + +# Check if FORCE_SCHEMA_UPDATE is set to true +if [ "$FORCE_SCHEMA_UPDATE" = "true" ]; then + echo "Forcing schema update..." + php bin/console doctrine:schema:update --force +fi + +# Execute the CMD from the Dockerfile +exec "$@" From 0424a85e2ec89756764afc1e0e69ea76b96ffd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ig=C3=B3n?= Date: Thu, 6 Mar 2025 12:57:36 +0100 Subject: [PATCH 2/9] change entrypoint location --- docker/php/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 0233faa..7e4b67b 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -54,9 +54,8 @@ RUN composer install --prefer-dist --no-scripts --no-dev --optimize-autoloader EXPOSE 9000 -COPY entrypoint.sh /usr/bin -RUN chmod +x /usr/bin/entrypoint.sh +COPY --chmod=+x entrypoint.sh /usr/local/bin/entrypoint.sh -ENTRYPOINT ["/usr/bin/entrypoint.sh"] +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["php-fpm"] From 065940a125dfccc90678bd4ea9bfcd8644bf772f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ig=C3=B3n?= Date: Thu, 6 Mar 2025 13:47:37 +0100 Subject: [PATCH 3/9] fix layer of entrypoint --- docker/php/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 7e4b67b..6c5d962 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -47,6 +47,9 @@ FROM base AS prod COPY . /app RUN chown -R www-data:www-data /app +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + USER www-data WORKDIR /app @@ -54,8 +57,6 @@ RUN composer install --prefer-dist --no-scripts --no-dev --optimize-autoloader EXPOSE 9000 -COPY --chmod=+x entrypoint.sh /usr/local/bin/entrypoint.sh - ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["php-fpm"] From c21cb3c39ee88ec070aa39a58652730b9f34f05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ig=C3=B3n?= Date: Thu, 6 Mar 2025 14:34:19 +0100 Subject: [PATCH 4/9] move entrypoint.sh to docker/php/entrypoint.sh --- docker/php/Dockerfile | 2 +- entrypoint.sh => docker/php/entrypoint.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename entrypoint.sh => docker/php/entrypoint.sh (100%) diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 6c5d962..dd8ab34 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -47,7 +47,7 @@ FROM base AS prod COPY . /app RUN chown -R www-data:www-data /app -COPY entrypoint.sh /usr/local/bin/entrypoint.sh +COPY /docker/php/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh USER www-data diff --git a/entrypoint.sh b/docker/php/entrypoint.sh similarity index 100% rename from entrypoint.sh rename to docker/php/entrypoint.sh From bef17962465f74ab337c236d132093113bbe2dc4 Mon Sep 17 00:00:00 2001 From: cmorenov1402 Date: Tue, 11 Mar 2025 11:50:16 +0100 Subject: [PATCH 5/9] Create Gateway Charge Extension --- config/services.yaml | 6 +++ src/Doctrine/GatewayChargeExtension.php | 56 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/Doctrine/GatewayChargeExtension.php diff --git a/config/services.yaml b/config/services.yaml index 1b5c0d3..210de7f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -88,5 +88,11 @@ services: - !tagged 'app.mapping.map_provider' - !tagged 'app.mapping.map_transformer' + App\Doctrine\GatewayChargeExtension: + arguments: [ '@security.helper' ] + tags: + - { name: 'api_platform.doctrine.orm.query_extension.collection' } + - { name: 'api_platform.doctrine.orm.query_extension.item' } + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Doctrine/GatewayChargeExtension.php b/src/Doctrine/GatewayChargeExtension.php new file mode 100644 index 0000000..82ab403 --- /dev/null +++ b/src/Doctrine/GatewayChargeExtension.php @@ -0,0 +1,56 @@ +security = $security; + } + + /** + * Method that applies the filter + */ + public function addFilter(QueryBuilder $queryBuilder, string $resourceClass) + { + if ($resourceClass !== Charge::class) { + return; // Just apply the filter to GatewayCharge + } + + $user = $this->security->getUser(); + + if (!$user || !$user instanceof \App\Entity\User\User) { + return; // If the user is not authenticated or not an instance of User, we do not apply the filter + } + + $rootAlias = $queryBuilder->getRootAliases()[0]; + + // Filter by the user based on the relationship with Checkout or Accounting + $queryBuilder + ->leftJoin("$rootAlias.checkout", "c") // Relationship with Gatewaycheckout + ->leftJoin("c.origin", "co") // Relationship with Accounting + ->leftJoin("co.user", "u") // Relationship with User + ->andWhere("u.id = :userId") // Filter by the user in the Origin entity (accounting) + ->setParameter('userId', $user->getId()); // Establishes the authenticated user parameter + } + + public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, \ApiPlatform\Metadata\Operation|null $operation = null, array $context = []): void + { + $this->addFilter($queryBuilder, $resourceClass); + } + + public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, \ApiPlatform\Metadata\Operation|null $operation = null, array $context = []): void + { + $this->addFilter($queryBuilder, $resourceClass); + } +} From 39e89f5d968011df30c2123b259df452d0ced201 Mon Sep 17 00:00:00 2001 From: cmorenov1402 Date: Tue, 11 Mar 2025 15:36:28 +0100 Subject: [PATCH 6/9] Filter by admin --- src/Doctrine/GatewayChargeExtension.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Doctrine/GatewayChargeExtension.php b/src/Doctrine/GatewayChargeExtension.php index 82ab403..9d14b7b 100644 --- a/src/Doctrine/GatewayChargeExtension.php +++ b/src/Doctrine/GatewayChargeExtension.php @@ -11,15 +11,15 @@ use Symfony\Bundle\SecurityBundle\Security; final class GatewayChargeExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface { - private Security $security; + public function __construct(Security $security) { $this->security = $security; } /** - * Method that applies the filter + * Method that applies the filter. */ public function addFilter(QueryBuilder $queryBuilder, string $resourceClass) { @@ -31,25 +31,30 @@ final class GatewayChargeExtension implements QueryCollectionExtensionInterface, if (!$user || !$user instanceof \App\Entity\User\User) { return; // If the user is not authenticated or not an instance of User, we do not apply the filter - } + } + + // If the user has ROLE_ADMIN, we dont filter the results + if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { + return; + } $rootAlias = $queryBuilder->getRootAliases()[0]; - // Filter by the user based on the relationship with Checkout or Accounting + // Filter by the owner user $queryBuilder - ->leftJoin("$rootAlias.checkout", "c") // Relationship with Gatewaycheckout - ->leftJoin("c.origin", "co") // Relationship with Accounting - ->leftJoin("co.user", "u") // Relationship with User - ->andWhere("u.id = :userId") // Filter by the user in the Origin entity (accounting) + ->leftJoin("$rootAlias.checkout", 'c') // Relationship with Gatewaycheckout + ->leftJoin('c.origin', 'co') // Relationship with Accounting + ->leftJoin('co.user', 'u') // Relationship with User + ->andWhere('u.id = :userId') // Filter by the user in the Origin entity (accounting) ->setParameter('userId', $user->getId()); // Establishes the authenticated user parameter } - public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, \ApiPlatform\Metadata\Operation|null $operation = null, array $context = []): void + public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?\ApiPlatform\Metadata\Operation $operation = null, array $context = []): void { $this->addFilter($queryBuilder, $resourceClass); } - public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, \ApiPlatform\Metadata\Operation|null $operation = null, array $context = []): void + public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?\ApiPlatform\Metadata\Operation $operation = null, array $context = []): void { $this->addFilter($queryBuilder, $resourceClass); } From 20c0bc49afe1778d986b84f03bdc91276591890a Mon Sep 17 00:00:00 2001 From: cmorenov1402 Date: Wed, 12 Mar 2025 09:31:41 +0100 Subject: [PATCH 7/9] Resolves inconsistency with gatewayCharge --- src/ApiResource/Gateway/ChargeApiResource.php | 4 ++++ src/Doctrine/GatewayChargeExtension.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ApiResource/Gateway/ChargeApiResource.php b/src/ApiResource/Gateway/ChargeApiResource.php index a3e6dd7..620af8f 100644 --- a/src/ApiResource/Gateway/ChargeApiResource.php +++ b/src/ApiResource/Gateway/ChargeApiResource.php @@ -2,6 +2,7 @@ namespace App\ApiResource\Gateway; +use ApiPlatform\Doctrine\Orm\Filter; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata as API; use App\ApiResource\Accounting\AccountingApiResource; @@ -20,6 +21,7 @@ use Symfony\Component\Validator\Constraints as Assert; provider: ApiResourceStateProvider::class )] #[API\Get()] +#[API\GetCollection()] class ChargeApiResource { #[API\ApiProperty(writable: false, identifier: true)] @@ -59,5 +61,7 @@ class ChargeApiResource * It is money before fees and taxes, not accountable. */ #[Assert\NotBlank()] + #[API\ApiFilter(Filter\RangeFilter::class, properties: ['money.amount'])] + #[API\ApiFilter(Filter\SearchFilter::class, properties: ['money.amount' => 'exact'])] public Money $money; } diff --git a/src/Doctrine/GatewayChargeExtension.php b/src/Doctrine/GatewayChargeExtension.php index 9d14b7b..fce9da8 100644 --- a/src/Doctrine/GatewayChargeExtension.php +++ b/src/Doctrine/GatewayChargeExtension.php @@ -42,8 +42,8 @@ final class GatewayChargeExtension implements QueryCollectionExtensionInterface, // Filter by the owner user $queryBuilder - ->leftJoin("$rootAlias.checkout", 'c') // Relationship with Gatewaycheckout - ->leftJoin('c.origin', 'co') // Relationship with Accounting + ->leftJoin("$rootAlias.checkout", 'c') // Relationship with Gatewaycheckout + ->leftJoin('c.origin', 'co') // Relationship with Accounting ->leftJoin('co.user', 'u') // Relationship with User ->andWhere('u.id = :userId') // Filter by the user in the Origin entity (accounting) ->setParameter('userId', $user->getId()); // Establishes the authenticated user parameter From 905eb2d4a11d76f35fbff0862a8040d4dd5d4379 Mon Sep 17 00:00:00 2001 From: cmorenov1402 Date: Wed, 12 Mar 2025 13:34:24 +0100 Subject: [PATCH 8/9] Prohibit access to anonymous users --- config/services.yaml | 6 --- src/ApiResource/Gateway/ChargeApiResource.php | 4 +- src/Doctrine/GatewayChargeExtension.php | 38 +++++++++++-------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 210de7f..1b5c0d3 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -88,11 +88,5 @@ services: - !tagged 'app.mapping.map_provider' - !tagged 'app.mapping.map_transformer' - App\Doctrine\GatewayChargeExtension: - arguments: [ '@security.helper' ] - tags: - - { name: 'api_platform.doctrine.orm.query_extension.collection' } - - { name: 'api_platform.doctrine.orm.query_extension.item' } - # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/ApiResource/Gateway/ChargeApiResource.php b/src/ApiResource/Gateway/ChargeApiResource.php index 620af8f..9136e83 100644 --- a/src/ApiResource/Gateway/ChargeApiResource.php +++ b/src/ApiResource/Gateway/ChargeApiResource.php @@ -21,7 +21,9 @@ use Symfony\Component\Validator\Constraints as Assert; provider: ApiResourceStateProvider::class )] #[API\Get()] -#[API\GetCollection()] +#[API\GetCollection( + security: "is_granted('IS_AUTHENTICATED_FULLY')" +)] class ChargeApiResource { #[API\ApiProperty(writable: false, identifier: true)] diff --git a/src/Doctrine/GatewayChargeExtension.php b/src/Doctrine/GatewayChargeExtension.php index fce9da8..4ab30bc 100644 --- a/src/Doctrine/GatewayChargeExtension.php +++ b/src/Doctrine/GatewayChargeExtension.php @@ -11,12 +11,7 @@ use Symfony\Bundle\SecurityBundle\Security; final class GatewayChargeExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface { - private Security $security; - - public function __construct(Security $security) - { - $this->security = $security; - } + public function __construct(private Security $security) {} /** * Method that applies the filter. @@ -29,12 +24,12 @@ final class GatewayChargeExtension implements QueryCollectionExtensionInterface, $user = $this->security->getUser(); - if (!$user || !$user instanceof \App\Entity\User\User) { - return; // If the user is not authenticated or not an instance of User, we do not apply the filter + if (!$user instanceof \App\Entity\User\User) { + return; // If the user is not an instance of User, we do not apply the filter } // If the user has ROLE_ADMIN, we dont filter the results - if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { + if ($user->hasRoles(['ROLE_ADMIN'])) { return; } @@ -42,20 +37,31 @@ final class GatewayChargeExtension implements QueryCollectionExtensionInterface, // Filter by the owner user $queryBuilder - ->leftJoin("$rootAlias.checkout", 'c') // Relationship with Gatewaycheckout - ->leftJoin('c.origin', 'co') // Relationship with Accounting + ->leftJoin("$rootAlias.checkout", 'c') // Relationship with Gatewaycheckout + ->leftJoin('c.origin', 'co') // Relationship with Accounting ->leftJoin('co.user', 'u') // Relationship with User ->andWhere('u.id = :userId') // Filter by the user in the Origin entity (accounting) - ->setParameter('userId', $user->getId()); // Establishes the authenticated user parameter + ->setParameter('userId', $user->getId()); } - public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?\ApiPlatform\Metadata\Operation $operation = null, array $context = []): void - { + public function applyToCollection( + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + ?\ApiPlatform\Metadata\Operation $operation = null, + array $context = [], + ): void { $this->addFilter($queryBuilder, $resourceClass); } - public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?\ApiPlatform\Metadata\Operation $operation = null, array $context = []): void - { + public function applyToItem( + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + array $identifiers, + ?\ApiPlatform\Metadata\Operation $operation = null, + array $context = [], + ): void { $this->addFilter($queryBuilder, $resourceClass); } } From b09085437de1bfd9b27572de2f03ac3f8c023c9f Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 14 Mar 2025 10:55:04 +0100 Subject: [PATCH 9/9] Fix ProjectsBudgetPump --- src/Benzina/ProjectsBudgetPump.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Benzina/ProjectsBudgetPump.php b/src/Benzina/ProjectsBudgetPump.php index 2d0c037..52908b0 100644 --- a/src/Benzina/ProjectsBudgetPump.php +++ b/src/Benzina/ProjectsBudgetPump.php @@ -6,6 +6,7 @@ use App\Entity\Money; use App\Entity\Project\BudgetItem; use App\Entity\Project\BudgetItemType; use App\Entity\Project\Project; +use App\Entity\Project\ProjectDeadline; use App\Repository\Project\ProjectRepository; use Goteo\Benzina\Pump\ArrayPumpTrait; use Goteo\Benzina\Pump\DoctrinePumpTrait; @@ -59,14 +60,8 @@ class ProjectsBudgetPump implements PumpInterface $budgetItem->setType($this->getCostType($record)); $budgetItem->setTitle($record['cost']); $budgetItem->setDescription($record['description'] ?? $record['cost']); - - $money = new Money($record['amount'] * 100, 'EUR'); - - if ($record['required'] > 0) { - $budgetItem->setOptimum($money); - } else { - $budgetItem->setMinimum($money); - } + $budgetItem->setMoney(new Money($record['amount'] * 100, 'EUR')); + $budgetItem->setDeadline($this->getDeadline($record)); $this->persist($budgetItem, $context); } @@ -87,4 +82,13 @@ class ProjectsBudgetPump implements PumpInterface return BudgetItemType::Infrastructure; } } + + private function getDeadline(array $record): ProjectDeadline + { + if ($record['required'] > 0) { + return ProjectDeadline::Minimum; + } + + return ProjectDeadline::Optimum; + } }