Skip to content

Commit

Permalink
Make hidden Type of Service field editable again
Browse files Browse the repository at this point in the history
Prior to this change, the Type of Service field was hidden and not
editable because users could be confused by certain internal SURF types,
such as SURF or Recommended.
This change hides those types from the UI and ensures they cannot be
modified from SP Dashboard.

Fixes #1322
  • Loading branch information
johanib committed Jan 27, 2025
1 parent dc8fe03 commit 3347b0a
Show file tree
Hide file tree
Showing 21 changed files with 565 additions and 24 deletions.
4 changes: 0 additions & 4 deletions assets/scss/pages/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,3 @@
.hidden {
display: none;
}

div.form-row:has(#dashboard_bundle_entity_type_metadata_typeOfService) {
display: none;
}
6 changes: 4 additions & 2 deletions assets/type_of_service.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[
{
"typeEn" : "SURF",
"typeNl" : "SURF"
"typeNl" : "SURF",
"typeHidden": true
},
{
"typeEn" : "Education",
Expand All @@ -25,7 +26,8 @@
},
{
"typeEn" : "Recommended",
"typeNl" : "Aangeraden"
"typeNl" : "Aangeraden",
"typeHidden": true
},
{
"typeEn" : "Productivity",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,15 @@ class SaveOidcngEntityCommand implements SaveEntityCommandInterface
#[Assert\Length(max: 300)]
private $applicationUrl;

/**
* // phpcs:ignore
* `new Assert\NotBlank(),` TODO enable in https://github.com/SURFnet/sp-dashboard/issues/1322
*/
/** @var TypeOfService[] */
#[Assert\All([
new Assert\NotBlank(),
new Assert\Type(type: TypeOfService::class),
])]
#[Assert\Count(
min: 1,
max: 3,
minMessage: 'validator.type-of-service.min',
maxMessage: 'validator.type-of-service.max',
)]
private array $typeOfService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,15 @@ class SaveSamlEntityCommand implements SaveEntityCommandInterface
#[Assert\Length(max: 300)]
private ?string $applicationUrl = null;

/**
* // phpcs:ignore
* `new Assert\NotBlank(),` TODO enable in https://github.com/SURFnet/sp-dashboard/issues/1322
* @var TypeOfService[]
*/
/** @var TypeOfService[] */
#[Assert\All([
new Assert\NotBlank(),
new Assert\Type(type: TypeOfService::class),
])]
#[Assert\Count(
min: 1,
max: 3,
minMessage: 'validator.type-of-service.min',
maxMessage: 'validator.type-of-service.max',
)]
private array $typeOfService = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
use Surfnet\ServiceProviderDashboard\Domain\Entity\Entity\JiraTicketNumber;
use Surfnet\ServiceProviderDashboard\Domain\Repository\EntityChangeRequestRepository;
use Surfnet\ServiceProviderDashboard\Domain\Service\ContractualBaseService;
use Surfnet\ServiceProviderDashboard\Domain\Service\TypeOfServiceService;
use Surfnet\ServiceProviderDashboard\Infrastructure\HttpClient\Exceptions\RuntimeException\PublishMetadataException;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class EntityChangeRequestCommandHandler implements CommandHandler
{
private readonly string $summaryTranslationKey;
Expand All @@ -43,6 +47,7 @@ class EntityChangeRequestCommandHandler implements CommandHandler
public function __construct(
private readonly EntityChangeRequestRepository $repository,
private readonly ContractualBaseService $contractualBaseHelper,
private readonly TypeOfServiceService $typeOfServiceService,
private readonly EntityServiceInterface $entityService,
private readonly TicketService $ticketService,
private readonly RequestStack $requestStack,
Expand All @@ -67,14 +72,18 @@ public function handle(PublishProductionCommandInterface $command): void
throw new EntityNotFoundException('Unable to request changes to a unkown entity in Manage');
}
$pristineEntity = $this->entityService->getPristineManageEntityById($entity->getId(), $entity->getEnvironment());

try {
$this->logger->info(
sprintf(
'Requesting changes in production environment for entity: "%s"',
$entity->getMetaData()->getNameEn()
)
);

$this->contractualBaseHelper->writeContractualBase($entity);
$entity->getMetaData()?->getCoin()->setTypeOfService($this->typeOfServiceService->restoreHiddenTypes($entity, $pristineEntity));

// Create the Jira ticket (we need the ticket id for the revision notes in manage later on)
$ticket = $this->ticketService->createJiraTicket(
$entity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use Surfnet\ServiceProviderDashboard\Domain\Entity\ManageEntity;
use Surfnet\ServiceProviderDashboard\Domain\Repository\PublishEntityRepository;
use Surfnet\ServiceProviderDashboard\Domain\Service\ContractualBaseService;
use Surfnet\ServiceProviderDashboard\Domain\Service\TypeOfServiceService;
use Surfnet\ServiceProviderDashboard\Infrastructure\HttpClient\Exceptions\RuntimeException\PublishMetadataException;
use Symfony\Component\HttpFoundation\RequestStack;

Expand All @@ -45,6 +46,7 @@ class PublishEntityProductionCommandHandler implements CommandHandler
public function __construct(
private readonly PublishEntityRepository $publishClient,
private readonly ContractualBaseService $contractualBaseHelper,
private readonly TypeOfServiceService $typeOfServiceService,
private readonly EntityServiceInterface $entityService,
private readonly TicketService $ticketService,
private readonly RequestStack $requestStack,
Expand Down Expand Up @@ -77,14 +79,18 @@ public function handle(PublishProductionCommandInterface $command): void
// The entity as it is now known in Manage
$pristineEntity = $this->entityService->getPristineManageEntityById($entity->getId(), $entity->getEnvironment());
}

try {
$this->logger->info(
sprintf(
'Publishing entity "%s" to Manage in production environment',
$entity->getMetaData()->getNameEn()
)
);

$this->contractualBaseHelper->writeContractualBase($entity);
$entity->getMetaData()?->getCoin()->setTypeOfService($this->typeOfServiceService->restoreHiddenTypes($entity, $pristineEntity));

$publishResponse = $this->publishClient->publish(
$entity,
$pristineEntity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Surfnet\ServiceProviderDashboard\Domain\Entity\ManageEntity;
use Surfnet\ServiceProviderDashboard\Domain\Repository\PublishEntityRepository;
use Surfnet\ServiceProviderDashboard\Domain\Service\ContractualBaseService;
use Surfnet\ServiceProviderDashboard\Domain\Service\TypeOfServiceService;
use Surfnet\ServiceProviderDashboard\Infrastructure\HttpClient\Exceptions\RuntimeException\PublishMetadataException;
use Symfony\Component\HttpFoundation\RequestStack;
use function array_key_exists;
Expand All @@ -36,6 +37,7 @@ class PublishEntityTestCommandHandler implements CommandHandler
public function __construct(
private readonly PublishEntityRepository $publishClient,
private readonly ContractualBaseService $contractualBaseHelper,
private readonly TypeOfServiceService $typeOfServiceService,
private readonly EntityServiceInterface $entityService,
private readonly LoggerInterface $logger,
private readonly RequestStack $requestStack,
Expand All @@ -54,13 +56,17 @@ public function handle(PublishEntityTestCommand $command): void
$pristineEntity = $this->entityService->getPristineManageEntityById($entity->getId(), $entity->getEnvironment());
$this->contractualBaseHelper->writeContractualBaseForTestEntity($entity, $pristineEntity);
}

try {
$this->logger->info(
sprintf(
'Publishing entity "%s" to Manage in test environment',
$entity->getMetaData()->getNameEn()
)
);

$entity->getMetaData()?->getCoin()->setTypeOfService($this->typeOfServiceService->restoreHiddenTypes($entity, $pristineEntity));

$publishResponse = $this->publishClient->publish(
$entity,
$pristineEntity,
Expand Down
10 changes: 10 additions & 0 deletions src/Surfnet/ServiceProviderDashboard/Domain/Entity/Entity/Coin.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public function getTypeOfService(): TypeOfServiceCollection
return $this->typeOfService;
}

public function hasTypeOfService(): bool
{
return $this->typeOfService !== null;
}

public function getContractualBase(): ?string
{
return $this->contractualBase;
Expand Down Expand Up @@ -176,4 +181,9 @@ public function asArray(): array
'metaDataFields.coin:ss:idp_visible_only' => $this->isIdpVisibleOnly(),
];
}

public function setTypeOfService(TypeOfServiceCollection $typeOfService): void
{
$this->typeOfService = $typeOfService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ private function load(): void
}
$this->collection = new TypeOfServiceCollection();
foreach ($data as $entry) {
$typeOfService = new TypeOfService($entry->typeEn, $entry->typeNl);
$typeHidden = $entry?->typeHidden ?? false;
$typeOfService = new TypeOfService($entry->typeEn, $entry->typeNl, $typeHidden);
$this->collection->add($typeOfService);
}
}
Expand All @@ -69,7 +70,7 @@ private function load(): void
*/
public function getTypesOfServiceChoices(): array
{
return $this->collection->getArray();
return $this->collection->filterHidden()->getArray();
}

public function findByEnglishTypeOfService(string $enTos): ?TypeOfService
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types = 1);

/**
* Copyright 2025 SURFnet B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Surfnet\ServiceProviderDashboard\Domain\Service;

use Surfnet\ServiceProviderDashboard\Domain\Entity\ManageEntity;
use Surfnet\ServiceProviderDashboard\Domain\ValueObject\TypeOfServiceCollection;

class TypeOfServiceService
{
public function restoreHiddenTypes(ManageEntity $entity, ?ManageEntity $pristineEntity): TypeOfServiceCollection
{
$typeOfService = new TypeOfServiceCollection();
if ($entity->getMetaData()?->getCoin()->hasTypeOfService() === true) {
$typeOfService = $entity->getMetaData()->getCoin()->getTypeOfService();
}

if ($pristineEntity && $pristineEntity->getMetaData()?->getCoin()->hasTypeOfService()) {
$pristineTypeOfService = $pristineEntity->getMetaData()->getCoin()->getTypeOfService();
}

return $this->mergeCollections($typeOfService, $pristineTypeOfService ?? null);
}

public function mergeCollections(
TypeOfServiceCollection $types,
?TypeOfServiceCollection $pristineTypes
): TypeOfServiceCollection {
$result = new TypeOfServiceCollection();

$this->addNonHiddenTypes($types, $result);

if ($pristineTypes === null) {
return $result;
}

$this->restoreHiddenPristineTypes($pristineTypes, $result);

return $result;
}

private function addNonHiddenTypes(TypeOfServiceCollection $types, TypeOfServiceCollection $result): void
{
foreach ($types->getArray() as $type) {
if (!$type->typeHidden && !$result->has($type->typeEn)) {
$result->add($type);
}
}
}

private function restoreHiddenPristineTypes(TypeOfServiceCollection $pristineTypes, TypeOfServiceCollection $result): void
{
foreach ($pristineTypes->getArray() as $pristineType) {
if ($pristineType->typeHidden && !$result->has($pristineType->typeEn)) {
$result->add($pristineType);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
public function __construct(
public string $typeEn,
public string $typeNl = '',
public bool $typeHidden = false
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,12 @@ public function getServicesAsArray(): array
}
return $services;
}

public function filterHidden(): self
{
$filteredCollection = new self();
$filteredCollection->types = array_values(array_filter($this->types, static fn ($type) => $type->typeHidden !== true));

return $filteredCollection;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,13 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'typeOfService',
ChoiceType::class,
[
'required' => false,
'required' => true,
'choices' => $this->typeOfServiceProvider->getTypesOfServiceChoices(),
'choice_value' => fn(TypeOfService $tos): string => $tos->typeEn,
'choice_label' => fn(TypeOfService $tos): string => $tos->typeEn,
'choice_attr' => fn(): array => [
'class' => 'decorated',
'data-parsley-mincheck' => 1,
'data-parsley-maxcheck' => 3,
],
'autocomplete' => true, // Enables the UX-Autocomplete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,13 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'typeOfService',
ChoiceType::class,
[
'required' => false,
'required' => true,
'choices' => $this->typeOfServiceProvider->getTypesOfServiceChoices(),
'choice_value' => fn(TypeOfService $tos): string => $tos->typeEn,
'choice_label' => fn(TypeOfService $tos): string => $tos->typeEn,
'choice_attr' => fn(): array => [
'class' => 'decorated',
'data-parsley-mincheck' => 1,
'data-parsley-maxcheck' => 3,
],
'autocomplete' => true, // Enables the UX-Autocomplete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ services:
arguments:
- '@surfnet.manage.client.publish_client.prod_environment'
- '@Surfnet\ServiceProviderDashboard\Domain\Service\ContractualBaseService'
- '@Surfnet\ServiceProviderDashboard\Domain\Service\TypeOfServiceService'
- '@Surfnet\ServiceProviderDashboard\Application\Service\EntityService'
- '@Surfnet\ServiceProviderDashboard\Application\Service\TicketService'
- '@request_stack'
Expand Down Expand Up @@ -596,6 +597,7 @@ services:
'@Surfnet\ServiceProviderDashboard\Infrastructure\DashboardBundle\Repository\ServiceRepository'

Surfnet\ServiceProviderDashboard\Domain\Service\ContractualBaseService:
Surfnet\ServiceProviderDashboard\Domain\Service\TypeOfServiceService:

Surfnet\ServiceProviderDashboard\Application\Service\AttributeServiceInterface:
'@Surfnet\ServiceProviderDashboard\Application\Service\AttributeService'
Expand Down
Loading

0 comments on commit 3347b0a

Please sign in to comment.