diff --git a/assets/js/components/mock/service_create_form.html b/assets/js/components/mock/service_create_form.html index 5041f1cb4..9ec105c3f 100644 --- a/assets/js/components/mock/service_create_form.html +++ b/assets/js/components/mock/service_create_form.html @@ -20,6 +20,12 @@

General

+
+

+ +

diff --git a/assets/js/components/mock/service_edit_form.html b/assets/js/components/mock/service_edit_form.html index 32b00aac9..6c9fe9756 100644 --- a/assets/js/components/mock/service_edit_form.html +++ b/assets/js/components/mock/service_edit_form.html @@ -23,6 +23,12 @@

General

+
+

+ +

diff --git a/migrations/Version20240514071702.php b/migrations/Version20240514071702.php index 7dc3e48c9..d6e6eef97 100644 --- a/migrations/Version20240514071702.php +++ b/migrations/Version20240514071702.php @@ -1,5 +1,21 @@ addSql('ALTER TABLE service DROP production_entities_enabled'); } public function down(Schema $schema): void { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE service ADD production_entities_enabled TINYINT(1) NOT NULL'); } -} +} \ No newline at end of file diff --git a/migrations/Version20241031120000.php b/migrations/Version20241031120000.php new file mode 100644 index 000000000..830b1d4ce --- /dev/null +++ b/migrations/Version20241031120000.php @@ -0,0 +1,33 @@ +connection->createSchemaManager()->listTableColumns('service'); + $columnExists = false; + foreach ($columns as $column) { + if ($column->getName() === 'production_entities_enabled') { + $columnExists = true; + break; + } + } + + if (!$columnExists) { + $this->addSql('ALTER TABLE service ADD production_entities_enabled TINYINT(1) NOT NULL DEFAULT 0'); + } + } +} \ No newline at end of file diff --git a/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/CreateServiceCommand.php b/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/CreateServiceCommand.php index ee0bc04b5..4861d8fec 100644 --- a/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/CreateServiceCommand.php +++ b/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/CreateServiceCommand.php @@ -58,6 +58,7 @@ class CreateServiceCommand implements Command */ private $institutionId; + private bool $productionEntitiesEnabled = false; private bool $privacyQuestionsEnabled = true; @@ -103,6 +104,11 @@ public function setName($name): void $this->name = $name; } + public function setProductionEntitiesEnabled(bool $enabled): void + { + $this->productionEntitiesEnabled = $enabled; + } + public function setPrivacyQuestionsEnabled(bool $privacyQuestionsEnabled): void { $this->privacyQuestionsEnabled = $privacyQuestionsEnabled; @@ -160,6 +166,11 @@ public function getName() return $this->name; } + public function isProductionEntitiesEnabled(): bool + { + return $this->productionEntitiesEnabled; + } + public function isPrivacyQuestionsEnabled(): bool { return $this->privacyQuestionsEnabled; diff --git a/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/EditServiceCommand.php b/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/EditServiceCommand.php index 0044897a9..cab482b7d 100644 --- a/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/EditServiceCommand.php +++ b/src/Surfnet/ServiceProviderDashboard/Application/Command/Service/EditServiceCommand.php @@ -42,6 +42,7 @@ public function __construct( #[SpDashboardAssert\UrnFormattedTeamName] #[Assert\NotBlank] private string $teamName, + private bool $productionEntitiesEnabled, private bool $privacyQuestionsEnabled, private bool $clientCredentialClientsEnabled, #[Assert\NotBlank] @@ -74,6 +75,11 @@ public function setName(string $name): void $this->name = $name; } + public function setProductionEntitiesEnabled(bool $enabled): void + { + $this->productionEntitiesEnabled = $enabled; + } + public function setPrivacyQuestionsEnabled(bool $privacyQuestionsEnabled): void { $this->privacyQuestionsEnabled = $privacyQuestionsEnabled; @@ -189,6 +195,11 @@ public function isPrivacyQuestionsAnswered(): bool return $this->privacyQuestionsAnswered; } + public function isProductionEntitiesEnabled(): bool + { + return $this->productionEntitiesEnabled; + } + public function isPrivacyQuestionsEnabled(): bool { return $this->privacyQuestionsEnabled; diff --git a/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/CreateServiceCommandHandler.php b/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/CreateServiceCommandHandler.php index e6e369ea8..c8e15d047 100644 --- a/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/CreateServiceCommandHandler.php +++ b/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/CreateServiceCommandHandler.php @@ -61,6 +61,7 @@ public function handle(CreateServiceCommand $command): void $service->setName($name); $service->setGuid($command->getGuid()); $service->setTeamName($fullTeamName); + $service->setProductionEntitiesEnabled($command->isProductionEntitiesEnabled()); $service->setPrivacyQuestionsEnabled($command->isPrivacyQuestionsEnabled()); $service->setClientCredentialClientsEnabled($command->isClientCredentialClientsEnabled()); $service->setServiceType($command->getServiceType()); diff --git a/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/EditServiceCommandHandler.php b/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/EditServiceCommandHandler.php index 1452dff37..83f829d12 100644 --- a/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/EditServiceCommandHandler.php +++ b/src/Surfnet/ServiceProviderDashboard/Application/CommandHandler/Service/EditServiceCommandHandler.php @@ -46,6 +46,7 @@ public function handle(EditServiceCommand $command): void $service->setName($command->getName()); $service->setGuid($command->getGuid()); + $service->setProductionEntitiesEnabled($command->isProductionEntitiesEnabled()); $service->setPrivacyQuestionsEnabled($command->isPrivacyQuestionsEnabled()); $service->setClientCredentialClientsEnabled($command->isClientCredentialClientsEnabled()); diff --git a/src/Surfnet/ServiceProviderDashboard/Application/ViewObject/Service.php b/src/Surfnet/ServiceProviderDashboard/Application/ViewObject/Service.php index 41367f54a..5443eda8b 100644 --- a/src/Surfnet/ServiceProviderDashboard/Application/ViewObject/Service.php +++ b/src/Surfnet/ServiceProviderDashboard/Application/ViewObject/Service.php @@ -28,6 +28,7 @@ public function __construct( private readonly bool $privacyQuestionsEnabled, private readonly EntityList $entityList, private readonly RouterInterface $router, + private readonly bool $productionEntitiesEnabled = false, ) { } @@ -39,6 +40,7 @@ public static function fromService(DomainService $service, EntityList $entityLis $service->isPrivacyQuestionsEnabled(), $entityList, $router, + $service->isProductionEntitiesEnabled() ); } @@ -71,4 +73,9 @@ public function hasTestEntities() : bool { return $this->getEntityList()->hasTestEntities(); } + + public function isProductionEntitiesEnabled(): bool + { + return $this->productionEntitiesEnabled || $this->hasTestEntities(); + } } diff --git a/src/Surfnet/ServiceProviderDashboard/Domain/Entity/Service.php b/src/Surfnet/ServiceProviderDashboard/Domain/Entity/Service.php index 3b677c75f..c708f0da6 100644 --- a/src/Surfnet/ServiceProviderDashboard/Domain/Entity/Service.php +++ b/src/Surfnet/ServiceProviderDashboard/Domain/Entity/Service.php @@ -22,10 +22,13 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Surfnet\ServiceProviderDashboard\Domain\Entity\PrivacyQuestions; use Gedmo\Mapping\Annotation as Gedmo; use Surfnet\ServiceProviderDashboard\Infrastructure\DashboardBundle\Repository\ServiceRepository; /** + * @package Surfnet\ServiceProviderDashboard\Entity + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.UnusedPrivateField) - the createdAt field is not used in code, but used by TPM's to check for * the creation date of a service. @@ -89,6 +92,9 @@ class Service #[ORM\Column(length: 255)] private $teamName; + #[ORM\Column(type: 'boolean')] + private bool $productionEntitiesEnabled = false; + #[ORM\Column(type: 'boolean')] private bool $privacyQuestionsEnabled = true; @@ -192,6 +198,11 @@ public function setName($name): void $this->name = $name; } + public function setProductionEntitiesEnabled(bool $enabled): void + { + $this->productionEntitiesEnabled = $enabled; + } + public function setPrivacyQuestionsEnabled(bool $privacyQuestionsEnabled): void { $this->privacyQuestionsEnabled = $privacyQuestionsEnabled; @@ -218,6 +229,11 @@ public function setPrivacyQuestions(PrivacyQuestions $privacyQuestions): void $this->privacyQuestions = $privacyQuestions; } + public function isProductionEntitiesEnabled(): bool + { + return $this->productionEntitiesEnabled; + } + public function isPrivacyQuestionsEnabled(): bool { return $this->privacyQuestionsEnabled; diff --git a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/EntityCreateController.php b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/EntityCreateController.php index 328067429..0e25cb0c3 100644 --- a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/EntityCreateController.php +++ b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/EntityCreateController.php @@ -82,6 +82,7 @@ public function type( $entityList = $this->entityService ->getEntityListForService($service) ->sortEntitiesByEnvironment(); + $isProductionEnabled = $entityList->hasTestEntities() || $service->isProductionEntitiesEnabled(); $form = $this->createForm(CreateNewEntityType::class, $formId); $form->handleRequest($request); @@ -129,6 +130,7 @@ public function type( 'environment' => $targetEnvironment, 'inputId' => $inputId, 'protocols' => $choices, + 'productionEnabled' => $isProductionEnabled, 'entities' => $entityList->getEntities(), 'manageId' => $formId, ] @@ -155,7 +157,9 @@ public function create(Request $request, $serviceId, $targetEnvironment, $type): $hasTestEntities = $this->entityService ->getEntityListForService($service)->hasTestEntities(); - if (!$hasTestEntities && $targetEnvironment !== Constants::ENVIRONMENT_TEST) { + if (!$service->isProductionEntitiesEnabled() && !$hasTestEntities + && $targetEnvironment !== Constants::ENVIRONMENT_TEST + ) { throw $this->createAccessDeniedException( 'You do not have access to create entities without publishing to the test environment first' ); diff --git a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/ServiceController.php b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/ServiceController.php index 12377f38f..ffa4eda50 100644 --- a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/ServiceController.php +++ b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Controller/ServiceController.php @@ -162,6 +162,7 @@ public function edit(Request $request, int $serviceId): RedirectResponse|Respons $service->getGuid(), $service->getName(), $service->getTeamName(), + $service->isProductionEntitiesEnabled(), $service->isPrivacyQuestionsEnabled(), $service->isClientCredentialClientsEnabled(), $service->getServiceType(), diff --git a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/DataFixtures/ORM/WebTestFixtures.php b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/DataFixtures/ORM/WebTestFixtures.php index 0adf01866..a24ac8e4e 100644 --- a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/DataFixtures/ORM/WebTestFixtures.php +++ b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/DataFixtures/ORM/WebTestFixtures.php @@ -34,9 +34,11 @@ class WebTestFixtures extends Fixture public function load(ObjectManager $manager): void { $service = $this->createService('SURFnet', self::TEAMNAME_SURF); + $service->setProductionEntitiesEnabled(false); $manager->persist($service); $service = $this->createService('Ibuildings B.V.', self::TEAMNAME_IBUILDINGS); + $service->setProductionEntitiesEnabled(true); $service->setPrivacyQuestionsEnabled(true); $service->setClientCredentialClientsEnabled(true); $manager->persist($service); diff --git a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/CreateServiceType.php b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/CreateServiceType.php index d58233d4f..731426401 100644 --- a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/CreateServiceType.php +++ b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/CreateServiceType.php @@ -73,6 +73,13 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'attr' => ['class' => 'institution-id-container'], ] ) + ->add( + 'productionEntitiesEnabled', + CheckboxType::class, + [ + 'required' => false, + ] + ) ->add( 'privacyQuestionsEnabled', CheckboxType::class, diff --git a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/EditServiceType.php b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/EditServiceType.php index ac03bbcc8..3763c3df3 100644 --- a/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/EditServiceType.php +++ b/src/Surfnet/ServiceProviderDashboard/Infrastructure/DashboardBundle/Form/Service/EditServiceType.php @@ -69,6 +69,13 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'attr' => ['class' => 'institution-id-container'], ] ) + ->add( + 'productionEntitiesEnabled', + CheckboxType::class, + [ + 'required' => false, + ] + ) ->add( 'privacyQuestionsEnabled', CheckboxType::class, diff --git a/templates/EntityModal/addEntityModal.html.twig b/templates/EntityModal/addEntityModal.html.twig index 5a8ea2f39..bf1433da4 100644 --- a/templates/EntityModal/addEntityModal.html.twig +++ b/templates/EntityModal/addEntityModal.html.twig @@ -7,7 +7,7 @@ {% set fieldName = manageId ~ '_environment' %}
  • {% set fieldId = manageId ~ '_prod' %} - +
  • diff --git a/templates/Service/overview.html.twig b/templates/Service/overview.html.twig index c26a3b401..058ec0ebd 100644 --- a/templates/Service/overview.html.twig +++ b/templates/Service/overview.html.twig @@ -21,7 +21,9 @@ {% set testId = "add-for-test-" ~ shortName %} {% set productionId = "add-for-production-" ~ shortName %} - + {% if service.isProductionEntitiesEnabled %} + + {% endif %}

    {{ service.name }}

      @@ -112,6 +114,7 @@ {{ 'service.overview.entitylist.empty'|trans }} {% endif %} + {% if service.isProductionEntitiesEnabled %} + {% endif %} {# Test entities #} @@ -178,7 +182,9 @@
    - + {% if service.isProductionEntitiesEnabled %} + + {% endif %}

    diff --git a/tests/integration/Application/CommandHandler/Service/CreateServiceCommandHandlerTest.php b/tests/integration/Application/CommandHandler/Service/CreateServiceCommandHandlerTest.php index 1ad84e501..46f0cc80f 100644 --- a/tests/integration/Application/CommandHandler/Service/CreateServiceCommandHandlerTest.php +++ b/tests/integration/Application/CommandHandler/Service/CreateServiceCommandHandlerTest.php @@ -80,6 +80,7 @@ public function test_it_can_process_a_create_service_command() $service->setTeamName('team-foobar'); $service->setGuid('30dd879c-ee2f-11db-8314-0800200c9a66'); $service->setPrivacyQuestionsEnabled(true); + $service->setProductionEntitiesEnabled(true); $service->setServiceType('institution'); $service->setIntakeStatus('yes'); @@ -94,6 +95,7 @@ public function test_it_can_process_a_create_service_command() $command->setTeamManagerEmail('tiffany@aching.do'); $command->setGuid($service->getGuid()); $command->setPrivacyQuestionsEnabled($service->isPrivacyQuestionsEnabled()); + $command->setProductionEntitiesEnabled($service->isProductionEntitiesEnabled()); $command->setOrganizationNameEn($service->getOrganizationNameEn()); $command->setOrganizationNameNl($service->getOrganizationNameNl()); $command->setServiceType($service->getServiceType()); @@ -126,6 +128,7 @@ public function test_it_rejects_non_unique_create_service_command() $command->setTeamName('team-foobar'); $command->setTeamManagerEmail('tiffany@aching.do'); $command->setGuid('30dd879c-ee2f-11db-8314-0800200c9a66'); + $command->setProductionEntitiesEnabled(true); $command->setPrivacyQuestionsEnabled(false); $command->setServiceType('institution'); diff --git a/tests/integration/Application/CommandHandler/Service/EditServiceCommandHandlerTest.php b/tests/integration/Application/CommandHandler/Service/EditServiceCommandHandlerTest.php index 7413d35ac..5a587cc95 100644 --- a/tests/integration/Application/CommandHandler/Service/EditServiceCommandHandlerTest.php +++ b/tests/integration/Application/CommandHandler/Service/EditServiceCommandHandlerTest.php @@ -53,6 +53,7 @@ public function test_it_can_process_an_edit_service_command() 'Foobar', 'team-foobar', false, + false, true, 'institution', 'not-applicable', @@ -67,6 +68,7 @@ public function test_it_can_process_an_edit_service_command() $command->setTeamName('team-foobar'); $command->setGuid('30dd879c-ee2f-11db-8314-0800200c9a66'); $command->setPrivacyQuestionsEnabled(false); + $command->setProductionEntitiesEnabled(false); $command->setPrivacyQuestionsAnswered(true); $command->setServiceType('institution'); @@ -84,6 +86,7 @@ public function test_it_can_process_an_edit_service_command() $this->assertEquals('Foobar', $arg->getName()); $this->assertEquals('team-foobar', $arg->getTeamName()); $this->assertEquals('30dd879c-ee2f-11db-8314-0800200c9a66', $arg->getGuid()); + $this->assertEquals(false, $arg->isPrivacyQuestionsEnabled()); $this->assertEquals('no', $arg->getContractSigned()); $this->assertEquals('no', $arg->getSurfconextRepresentativeApproved()); $this->assertEquals('not-applicable', $arg->getIntakeStatus()); @@ -113,6 +116,7 @@ public function test_it_rejects_non_existing_service() 'Foobar', 'team-foobar', false, + false, true, 'institution', 'not-applicable', @@ -143,6 +147,7 @@ public function test_it_rejects_non_unique_edit_service_command() 'team-foobar', false, false, + false, 'institution', 'not-applicable', 'no',