Conditional App Activation
Introduction
Section titled “Introduction”Conditional App Activation is a runtime feature in yuuvis Shell (available since version 2.8.0) that allows you to dynamically control which apps are available to users based on backend apps availability and user permissions. Instead of building different client versions for different deployments, you can configure a single client application that adapts its available functionality based on the environment and user context. The yuuvis client CLI supports this feature since version 19.6.0, automatically scaffolding new client projects with the necessary configuration.
The Shell evaluates app and extension availability at startup. Client apps and extensions that don’t meet their requirements are not loaded at all - they won’t appear in navigation and their code is never initialized. This works through a combination of client-side configuration (defining backend app requirements in app.requirements.ts and permission constraints in app.routes.ts) and backend-side administration (where administrators enable or disable backend apps for specific tenants).
Why Conditional App Activation?
Section titled “Why Conditional App Activation?”Conditional App Activation solves several common challenges in enterprise application deployments:
Flexible Deployments Deploy the same client build across different environments with varying backend configurations. Client apps automatically adapt based on which backend apps administrators have enabled, without requiring code changes or separate builds.
Backend App Requirements Client apps and extensions can declare requirements for specific backend apps. If an administrator has not enabled a required backend app, or if it’s unavailable, the client app or extension is not loaded at all. This prevents users from accessing functionality that cannot work without its backend requirements and ensures that incomplete features don’t appear in the application.
Role-Based App Visibility Control which apps are visible based on user roles and permissions. Apps can define allowed or denied user authorities, ensuring users only see functionality they’re authorized to access.
Multi-Tenant Deployments Serve different feature sets to different customers or tenants using the same client application. Administrators can enable different sets of backend apps per tenant, determining which client apps are available without maintaining separate codebases.
Premium Features Implement tiered feature access where certain client apps require premium backend apps or specific license levels. Administrators control which backend apps are enabled based on the customer’s subscription level.
How It Works
Section titled “How It Works”The conditional app activation mechanism operates through collaboration between client-side configuration and backend administration:
Configuration Side:
- Client Development: Client developers define which client apps require the availability of which backend apps in
app.requirements.ts. Permission constraints for each client app are defined separately inapp.routes.ts. - Backend Administration: Administrators enable or disable backend apps for specific tenants based on deployment needs, license levels, or tenant requirements.
Runtime Evaluation:
-
Initialization: During Shell bootstrap, the client fetches the list of enabled backend apps from the server and retrieves user authentication details including permissions.
-
Evaluation: For each registered client app and extension, the framework evaluates two conditions:
- Are the required backend apps (defined in
app.requirements.ts) enabled and available? - Does the user have the required permissions (or lack forbidden permissions)?
- Are the required backend apps (defined in
-
Loading Control: Client apps and extensions that don’t meet their requirements are not loaded at all:
- Client apps: Neither the UI nor the extension service is initialized
- Extensions: The extension service is not initialized
- UI visibility: Apps are filtered from navigation menus and app launchers
- Route protection: App routes are guarded to prevent direct navigation
This separation of concerns allows client developers to define which client apps require which backend apps, while administrators control backend app availability for tenants without modifying the client.
Two Levels of Control
Section titled “Two Levels of Control”Conditional app activation provides two complementary mechanisms for controlling app availability. You can use either independently or combine them for fine-grained control.
Backend App Requirements
Section titled “Backend App Requirements”Client apps (the apps in your Shell-based client application) can declare requirements for backend apps through a requirements mapping. This is useful when a client app needs specific backend capabilities to function.
For example, an advanced search client app might require a specific backend app for search services. If the administrator has not enabled that backend app, or if it’s unavailable, the client app is not loaded at all - neither its UI nor its extension service.
Requirements are defined in your client implementation in app.requirements.ts as a mapping from client app IDs to required backend app identifiers. The framework checks these requirements against backend-provided availability information during Shell bootstrap.
Key characteristics:
- Client-side: Client developers define requirements in configuration files (
app.requirements.ts) - Backend-side: Administrators enable/disable backend apps for tenants
- Evaluated during Shell bootstrap against backend availability
- Client apps and extensions are not loaded if their required backend apps aren’t enabled
User Permissions
Section titled “User Permissions”Client apps can define which user authorities (roles) should have access through AppPermission configuration. This provides role-based access control at the app level.
Each app can specify:
- Allowed authorities: User must have at least one of these roles to access the app
- Denied authorities: User must NOT have any of these roles to access the app
For example, an administration client app might allow only users with the ADMIN role, while a reporting app might deny access to GUEST users.
Key characteristics:
- Defined in client implementation (typically in route configuration files like
app.routes.ts) - Evaluated against authenticated user’s authorities
- Combined with backend app requirements using AND logic
- Client apps are not loaded if permission requirements aren’t met
Combining both levels: When a client app or extension has both backend app requirements and permission constraints defined, both conditions must be satisfied for it to be loaded. A client app or extension is only loaded if its required backend apps are available (enabled by administrators) AND the user has appropriate permissions.
Implementation Guide
Section titled “Implementation Guide”This section shows you how to implement conditional app activation in your Shell application using practical examples.
Configuring Backend App Requirements
Section titled “Configuring Backend App Requirements”Backend app requirements define which backend apps must be enabled for a client app or extension to be loaded.
Step 1: Create Requirements Configuration
Section titled “Step 1: Create Requirements Configuration”Create an app.requirements.ts file in your application’s source folder (or use the automatically generated one):
import { Requirements } from '@yuuvis/client-core';
export const requirements: Requirements = { // Client app requires the "osdrive" backend app 'io.yuuvis.app.drive': ['osdrive'],
// Search app requires multiple backend apps (all must be enabled) 'com.example.app.inbox': ['inbox', 'workflows'],
// Extension also uses the same pattern 'com.example.extension.pdf': ['pdf-service']};Key points:
- The key is the client app or extension ID under which the app/extension is registered (found in the app manifest)
- The value is an array of backend app names (all must be enabled)
- Backend app names must match the keys returned by
/api/dms/apps - If any required backend app is missing or disabled, the client app/extension is not loaded
Step 2: Register Requirements at Bootstrap
Section titled “Step 2: Register Requirements at Bootstrap”Import and pass the requirements to the bootstrap function in main.ts:
import { bootstrapShellApplication } from '@yuuvis/client-shell-core';import { AppComponent } from './app/app.component';import { appConfig } from './app/app.config';import { requirements } from './app/app.requirements';
bootstrapShellApplication( AppComponent, appConfig, { enableFeatureConfig: true, enableObjectTypeConfig: true }, requirements // ← Pass requirements here).catch((err) => console.error(err));What happens:
- During bootstrap, the Shell fetches enabled backend apps from
/api/dms/apps - The requirements configuration is registered with the
FeatureService - Apps and extensions are evaluated against these requirements before loading
Configuring User Permissions
Section titled “Configuring User Permissions”User permissions control which users can access specific client apps based on their roles (authorities).
Configure App Permissions in Routes
Section titled “Configure App Permissions in Routes”Define permission constraints directly in your app route configuration:
import { Route } from '@angular/router';import { App, canMatchFeature } from '@yuuvis/client-shell-core';
export const apps: App[] = [ { id: 'com.example.app.admin', title: 'Administration', path: 'admin', iconName: 'admin_panel_settings', canMatch: [canMatchFeature], // ← Route guard checks permissions
// Permission configuration permissions: { allow: ['YUUVIS_TENANT_ADMIN', 'CUSTOM_SUPERUSER'], // User must have one of these roles deny: [] },
loadChildren: () => import('@custom/app-admin').then(m => m.AdminRoutes) },
{ id: 'io.custom.app.reports', title: 'Reports', path: 'reports', iconName: 'assessment', canMatch: [canMatchFeature],
// Deny specific roles permissions: { allow: [], // Empty = all users allowed (unless denied) deny: ['CUSTOM_GUEST'] // Users with GUEST role cannot access },
loadChildren: () => import('@yuuvis/app-reports').then(m => m.ReportsRoutes) },
{ id: 'io.yuuvis.app.drive', title: 'yuuvis explorer', path: 'drive', iconName: 'cloud_circle', canMatch: [canMatchFeature], // ← Always use this guard
// Combine both: allow AND deny permissions: { allow: ['YUUVIS_DEFAULT', 'YUUVIS_TENANT_ADMIN'], deny: ['CUSTOM_GUEST', 'CUSTOM_EXTERNAL'] },
loadChildren: () => import('@yuuvis/app-drive').then(m => m.DriveRoutes) }];Permission evaluation logic:
- permissions.allow: User must have at least ONE of these authorities
- permissions.deny: User must NOT have ANY of these authorities
- Both conditions must be true:
isAllowed AND NOT isDenied - If
permissions.allowis empty, all users are allowed (unless denied)
Important: Always Use the Feature Guard
Section titled “Important: Always Use the Feature Guard”The canMatchFeature guard is essential for conditional activation:
import { canMatchFeature } from '@yuuvis/client-shell-core';
{ id: 'com.example.app.myapp', canMatch: [canMatchFeature], // ← Required for conditional activation // ...}What the guard does:
- Checks backend app requirements (from
app.requirements.ts) - Checks user permissions (from
permissions.allow/permissions.denyin the route) - Prevents route matching if conditions aren’t met
- Blocks navigation even via direct URL
Filtering Available Apps
Section titled “Filtering Available Apps”In your app component, use the FeatureService to filter apps based on requirements and permissions:
import { Component, inject } from '@angular/core';import { ClientShellComponent } from '@yuuvis/client-shell';import { App, ClientShellConfig, FeatureService } from '@yuuvis/client-shell-core';import { apps } from './app/app.routes';
@Component({ selector: 'app-root', standalone: true, imports: [ClientShellComponent], template: ` <yuv-client-shell [apps]="apps" [config]="shellConfig"> </yuv-client-shell> `})export class AppComponent { #featureService = inject(FeatureService);
// Filter apps based on requirements and permissions apps: App[] = this.#featureService.getAvailableApps(apps);
shellConfig: ClientShellConfig = {};}How getAvailableApps() works:
- Takes all configured apps as input
- Filters out apps where backend requirements aren’t met
- Filters out apps where user permissions don’t allow access
- Returns only apps that should be visible and accessible
Result:
- Apps that don’t meet requirements/permissions are removed from navigation
- The Shell only displays apps the user can actually use