diff --git a/.env.ci b/.env.ci index 220adebbe..76464fc9f 100644 --- a/.env.ci +++ b/.env.ci @@ -195,4 +195,4 @@ KEYCLOAK_PROD_SCOPE_ENTRY_API_ID='d8a54568-26da-412b-a441-d5e2fad84478' #publiq- KEYCLOAK_PROD_SCOPE_UITPAS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#uitpas-scope SEARCH_BASE_URI=https://search-acc.uitdatabank.be/ -SEARCH_API_KEY= +SEARCH_API_KEY=deb306a6-6f46-4c98-89ce-b03ec4fd11e2 diff --git a/app/Auth0/Auth0ServiceProvider.php b/app/Auth0/Auth0ServiceProvider.php index e82458486..30432294d 100644 --- a/app/Auth0/Auth0ServiceProvider.php +++ b/app/Auth0/Auth0ServiceProvider.php @@ -4,12 +4,12 @@ namespace App\Auth0; -use App\Auth0\Jobs\UnblockClient; -use App\Auth0\Jobs\UnblockClientHandler; use App\Auth0\Jobs\BlockClient; use App\Auth0\Jobs\BlockClientHandler; use App\Auth0\Jobs\CreateMissingClients; use App\Auth0\Jobs\CreateMissingClientsHandler; +use App\Auth0\Jobs\UnblockClient; +use App\Auth0\Jobs\UnblockClientHandler; use App\Auth0\Listeners\BlockClients; use App\Auth0\Listeners\CreateClients; use App\Auth0\Listeners\UnblockClients; @@ -26,7 +26,6 @@ use App\Domain\Integrations\Events\IntegrationUrlCreated; use App\Domain\Integrations\Events\IntegrationUrlDeleted; use App\Domain\Integrations\Events\IntegrationUrlUpdated; -use App\Keycloak\KeycloakConfig; use Auth0\SDK\Configuration\SdkConfiguration; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Event; @@ -56,15 +55,14 @@ public function register(): void // Filter out tenants with missing config (consider them disabled) $tenantsConfig = array_filter( config('auth0.tenants'), - static fn (array $tenantConfig) => - $tenantConfig['domain'] !== '' && + static fn (array $tenantConfig) => $tenantConfig['domain'] !== '' && $tenantConfig['clientId'] !== '' && $tenantConfig['clientSecret'] !== '' ); // Create Auth0Tenant objects based on the tenants config keys $tenants = array_map( - static fn (string|int $tenant) => Auth0Tenant::from((string) $tenant), + static fn (string|int $tenant) => Auth0Tenant::from((string)$tenant), array_keys($tenantsConfig) ); @@ -93,7 +91,7 @@ public function register(): void ); }); - if (config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) { + if (config('auth0.enabled')) { // By default, the Auth0 integration is enabled. For testing purposes this can be disabled inside the .env file. // May always be registered even if there are no configured tenants, because in that case the cluster SDK will diff --git a/app/Domain/Integrations/Controllers/IntegrationController.php b/app/Domain/Integrations/Controllers/IntegrationController.php index 72cd433ac..b9ff79171 100644 --- a/app/Domain/Integrations/Controllers/IntegrationController.php +++ b/app/Domain/Integrations/Controllers/IntegrationController.php @@ -10,11 +10,11 @@ use App\Domain\Contacts\Repositories\ContactKeyVisibilityRepository; use App\Domain\Contacts\Repositories\ContactRepository; use App\Domain\Coupons\Repositories\CouponRepository; +use App\Domain\Integrations\FormRequests\KeyVisibilityUpgradeRequest; use App\Domain\Integrations\FormRequests\RequestActivationRequest; use App\Domain\Integrations\FormRequests\StoreContactRequest; use App\Domain\Integrations\FormRequests\StoreIntegrationRequest; use App\Domain\Integrations\FormRequests\StoreIntegrationUrlRequest; -use App\Domain\Integrations\FormRequests\KeyVisibilityUpgradeRequest; use App\Domain\Integrations\FormRequests\UpdateContactInfoRequest; use App\Domain\Integrations\FormRequests\UpdateIntegrationRequest; use App\Domain\Integrations\FormRequests\UpdateIntegrationUrlsRequest; @@ -23,27 +23,30 @@ use App\Domain\Integrations\IntegrationType; use App\Domain\Integrations\IntegrationUrl; use App\Domain\Integrations\KeyVisibility; +use App\Domain\Integrations\Mappers\KeyVisibilityUpgradeMapper; use App\Domain\Integrations\Mappers\OrganizationMapper; use App\Domain\Integrations\Mappers\OrganizerMapper; use App\Domain\Integrations\Mappers\StoreContactMapper; use App\Domain\Integrations\Mappers\StoreIntegrationMapper; use App\Domain\Integrations\Mappers\StoreIntegrationUrlMapper; -use App\Domain\Integrations\Mappers\KeyVisibilityUpgradeMapper; use App\Domain\Integrations\Mappers\UpdateContactInfoMapper; use App\Domain\Integrations\Mappers\UpdateIntegrationMapper; use App\Domain\Integrations\Mappers\UpdateIntegrationUrlsMapper; +use App\Domain\Integrations\Organizer; use App\Domain\Integrations\Repositories\IntegrationRepository; use App\Domain\Integrations\Repositories\IntegrationUrlRepository; use App\Domain\Integrations\Repositories\OrganizerRepository; use App\Domain\KeyVisibilityUpgrades\KeyVisibilityUpgrade; +use App\Domain\KeyVisibilityUpgrades\Repositories\KeyVisibilityUpgradeRepository; use App\Domain\Organizations\Repositories\OrganizationRepository; use App\Domain\Subscriptions\Repositories\SubscriptionRepository; -use App\Domain\KeyVisibilityUpgrades\Repositories\KeyVisibilityUpgradeRepository; use App\Http\Controllers\Controller; use App\ProjectAanvraag\ProjectAanvraagUrl; use App\Router\TranslatedRoute; +use App\Search\Sapi3\SearchService; use App\UiTiDv1\Repositories\UiTiDv1ConsumerRepository; use Carbon\Carbon; +use CultuurNet\SearchV3\ValueObjects\Organizer as SapiOrganizer; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\UniqueConstraintViolationException; use Illuminate\Http\RedirectResponse; @@ -70,6 +73,7 @@ public function __construct( private readonly Auth0ClientRepository $auth0ClientRepository, private readonly UiTiDv1ConsumerRepository $uitidV1ConsumerRepository, private readonly KeyVisibilityUpgradeRepository $keyVisibilityUpgradeRepository, + private readonly SearchService $searchClient, private readonly CurrentUser $currentUser ) { } @@ -170,12 +174,24 @@ public function show(string $id): Response $integration = $this->integrationRepository->getById(Uuid::fromString($id)); $oldCredentialsExpirationDate = $this->getExpirationDateForOldCredentials($integration->getKeyVisibilityUpgrade()); + $organizerIds = collect($integration->organizers())->map(fn (Organizer $organizer) => $organizer->organizerId->toString()); + $uitpasOrganizers = $this->searchClient->findUiTPASOrganizers(...$organizerIds)->getMember()?->getItems(); + $organizers = collect($uitpasOrganizers)->map(function (SapiOrganizer $organizer) { + $id = explode('/', $organizer->getId() ?? ''); + + return [ + 'id' => $id[count($id) - 1], + 'name' => $organizer->getName()?->getValues() ?? $id, + 'status' => $organizer->getWorkflowStatus() === 'ACTIVE' ? 'Live' : 'Test', + ]; + }); + return Inertia::render('Integrations/Detail', [ 'integration' => $integration->toArray(), 'oldCredentialsExpirationDate' => $oldCredentialsExpirationDate, 'email' => Auth::user()?->email, 'subscriptions' => $this->subscriptionRepository->all(), - 'organizers' => session('organizers'), + 'organizers' => $organizers, ]); } diff --git a/app/Domain/Integrations/Integration.php b/app/Domain/Integrations/Integration.php index 034a589bf..4b44cb5b4 100644 --- a/app/Domain/Integrations/Integration.php +++ b/app/Domain/Integrations/Integration.php @@ -31,6 +31,9 @@ final class Integration private ?Organization $organization; + /** @var array */ + private array $organizers; + /** @var array */ private array $uiTiDv1Consumers; @@ -57,6 +60,7 @@ public function __construct( $this->uiTiDv1Consumers = []; $this->auth0Clients = []; $this->keycloakClients = []; + $this->organizers = []; $this->organization = null; $this->keyVisibility = KeyVisibility::v2; $this->keyVisibilityUpgrade = null; @@ -122,6 +126,13 @@ public function withKeycloakClients(KeycloakClient ...$keycloakClients): self return $clone; } + public function withOrganizers(Organizer ...$organizers): self + { + $clone = clone $this; + $clone->organizers = $organizers; + return $clone; + } + public function withSubscription(Subscription $subscription): self { $clone = clone $this; @@ -166,6 +177,14 @@ public function organization(): ?Organization return $this->organization; } + /** + * @return array + */ + public function organizers(): array + { + return $this->organizers; + } + /** @return array */ public function uiTiDv1Consumers(): array { @@ -226,6 +245,7 @@ public function toArray(): array 'contacts' => $this->contacts, 'urls' => $this->urls, 'organization' => $this->organization, + 'organizers' => $this->organizers, 'authClients' => $this->auth0Clients, 'legacyAuthConsumers' => $this->uiTiDv1Consumers, 'keycloakClients' => $this->keycloakClients, diff --git a/app/Domain/Integrations/Models/IntegrationModel.php b/app/Domain/Integrations/Models/IntegrationModel.php index 1f6ff7c3a..f9305ab58 100644 --- a/app/Domain/Integrations/Models/IntegrationModel.php +++ b/app/Domain/Integrations/Models/IntegrationModel.php @@ -350,6 +350,11 @@ public function toDomain(): Integration ->get() ->map(fn (KeycloakClientModel $keycloakClientModel) => $keycloakClientModel->toDomain()) ->toArray() + )->withOrganizers( + ...$this->organizers() + ->get() + ->map(fn (OrganizerModel $organizerModel) => $organizerModel->toDomain()) + ->toArray() ); if ($this->keyVisibilityUpgrade) { diff --git a/app/Search/Sapi3/Sapi3SearchService.php b/app/Search/Sapi3/Sapi3SearchService.php index a9c0abfee..d2e60497f 100644 --- a/app/Search/Sapi3/Sapi3SearchService.php +++ b/app/Search/Sapi3/Sapi3SearchService.php @@ -4,6 +4,7 @@ namespace App\Search\Sapi3; +use CultuurNet\SearchV3\Parameter\Id; use CultuurNet\SearchV3\Parameter\Query; use CultuurNet\SearchV3\SearchClientInterface; use CultuurNet\SearchV3\SearchQuery; @@ -25,4 +26,19 @@ public function searchUiTPASOrganizer(string $name): PagedCollection return $this->searchClient->searchOrganizers($searchQuery); } + + public function findUiTPASOrganizers(string ...$ids): PagedCollection + { + $searchQuery = new SearchQuery(); + $searchQuery->setEmbed(true); + if (empty($ids)) { + return new PagedCollection(); + } + + foreach ($ids as $id) { + $searchQuery->addParameter(new Id($id)); + } + + return $this->searchClient->searchOrganizers($searchQuery); + } } diff --git a/app/Search/Sapi3/SearchService.php b/app/Search/Sapi3/SearchService.php index 4bce72119..66a8706e0 100644 --- a/app/Search/Sapi3/SearchService.php +++ b/app/Search/Sapi3/SearchService.php @@ -9,4 +9,5 @@ interface SearchService { public function searchUiTPASOrganizer(string $name): PagedCollection; + public function findUiTPASOrganizers(string ...$ids): PagedCollection; } diff --git a/resources/translations/en.json b/resources/translations/en.json index 70db4ceea..bdfbb22c4 100644 --- a/resources/translations/en.json +++ b/resources/translations/en.json @@ -229,6 +229,11 @@ "description": "Contacts can edit, activate, and delete the integration. Once deleted, a contact no longer has access to the integration." } }, + "organizers_info": { + "title": "Organizers", + "description": "Below you see an overview of the UiTdatabank organizers for which you can execute actions in the UiTPAS API.", + "add": "Add organizer" + }, "billing_info": { "title": { "billing": "Billing information", diff --git a/resources/translations/nl.json b/resources/translations/nl.json index 365479bbd..431257565 100644 --- a/resources/translations/nl.json +++ b/resources/translations/nl.json @@ -229,6 +229,11 @@ "description": "Contacten kunnen de integratie aanpassen, activeren en verwijderen. Eens verwijderd heeft een contact geen toegang meer tot de integratie." } }, + "organizers_info": { + "title": "Organisaties", + "description": "Hieronder vind je een overzicht van de UiTdatabank organisaties waarvoor je acties kan uitvoeren in de UiTPAS API.", + "add": "Organisatie toevoegen" + }, "billing_info": { "title": { "billing": "Facturatiegegevens", diff --git a/resources/ts/Components/CopyText.tsx b/resources/ts/Components/CopyText.tsx index a1e8b3e8a..d7a456f71 100644 --- a/resources/ts/Components/CopyText.tsx +++ b/resources/ts/Components/CopyText.tsx @@ -25,7 +25,7 @@ export const CopyText = ({ text, isSecret }: Props) => { }; return ( -
+
{ + const { t, i18n } = useTranslation(); + if (!organizers?.length) { + return null; + } + + return ( + <> + + {sectionName} + + {organizers.map((organizer) => ( + +
+

{organizer.name[i18n.language]}

+
+ +
+ {sectionName === "Live" && ( +
+ + +
+ )} +
+
+ ))} +
+ {sectionName === "Live" && ( + + {t("details.organizers_info.add")} + + )} +
+ + ); +}; + +export const OrganizersInfo = ({ organizers }: Props) => { + const { t } = useTranslation(); + const byStatus = groupBy(organizers, "status"); + + return ( + <> + + {t("details.organizers_info.title")} + +

{t("details.organizers_info.description")}

+ + + + ); +}; diff --git a/resources/ts/Pages/Integrations/Detail.tsx b/resources/ts/Pages/Integrations/Detail.tsx index 45ba2ada3..79861aa11 100644 --- a/resources/ts/Pages/Integrations/Detail.tsx +++ b/resources/ts/Pages/Integrations/Detail.tsx @@ -23,6 +23,8 @@ import { PricingPlanProvider } from "../../Context/PricingPlan"; import { useIsMobile } from "../../hooks/useIsMobile"; import { CouponInfoProvider } from "../../Context/CouponInfo"; import type { Integration } from "../../types/Integration"; +import { OrganizersInfo } from "../../Components/Integrations/Detail/OrganizersInfo"; +import type { Organizer } from "../../types/Organizer"; type Props = { integration: Integration; @@ -30,6 +32,7 @@ type Props = { subscriptions: Subscription[]; oldCredentialsExpirationDate: string; errors: Record; + organizers: Organizer[]; }; const Detail = ({ @@ -37,6 +40,7 @@ const Detail = ({ email, subscriptions, oldCredentialsExpirationDate, + organizers, errors, }: Props) => { const { t } = useTranslation(); @@ -175,6 +179,12 @@ const Detail = ({ duplicateContactErrorMessage={duplicateContactErrorMessage} /> + + +