File

apps/recallassess/recallassess-api/src/config/navigation/services/navigation-filter.service.ts

Description

Navigation Filter Service Filters navigation items based on user role, permissions, and feature flags

This service is designed to be extensible:

  • Add new filter criteria by extending NavigationFilterContext
  • Implement custom filtering logic in filterItems method
  • Support nested navigation items (children)

Index

Methods

Methods

filterItems
filterItems(items: RoleBasedNavigationItem[], context: NavigationFilterContext)

Filter navigation items based on context

Parameters :
Name Type Optional Description
items RoleBasedNavigationItem[] No
  • Array of navigation items to filter
context NavigationFilterContext No
  • Filter context containing role, permissions, etc.

Filtered array of navigation items

getItemsForRole
getItemsForRole(items: RoleBasedNavigationItem[], role: ParticipantRole | string)

Get navigation items for a specific role (convenience method)

Parameters :
Name Type Optional Description
items RoleBasedNavigationItem[] No
  • Array of navigation items
role ParticipantRole | string No
  • User role

Filtered navigation items for the role

Private hasRequiredFeatureFlags
hasRequiredFeatureFlags(userFlags: string[], requiredFlags: string[])

Check if user has required feature flags

Parameters :
Name Type Optional Description
userFlags string[] No
  • User's active feature flags
requiredFlags string[] No
  • Required feature flags (at least one must match)
Returns : boolean

true if user has at least one required feature flag

Private hasRequiredPermissions
hasRequiredPermissions(userPermissions: string[], requiredPermissions: string[])

Check if user has required permissions

Parameters :
Name Type Optional Description
userPermissions string[] No
  • User's permissions
requiredPermissions string[] No
  • Required permissions (at least one must match)
Returns : boolean

true if user has at least one required permission

Private hasRequiredRole
hasRequiredRole(userRole: ParticipantRole | string, allowedRoles: ParticipantRole[])

Check if user has required role

Parameters :
Name Type Optional Description
userRole ParticipantRole | string No
  • User's role
allowedRoles ParticipantRole[] No
  • Array of allowed roles
Returns : boolean

true if user has at least one of the allowed roles

Private processItem
processItem(item: RoleBasedNavigationItem, context: NavigationFilterContext)

Process a navigation item (filter children, clean metadata, etc.)

Parameters :
Name Type Optional Description
item RoleBasedNavigationItem No
  • Navigation item to process
context NavigationFilterContext No
  • Filter context

Processed navigation item

Private shouldShowItem
shouldShowItem(item: RoleBasedNavigationItem, context: NavigationFilterContext)

Determine if an item should be shown based on filter context

Parameters :
Name Type Optional Description
item RoleBasedNavigationItem No
  • Navigation item to check
context NavigationFilterContext No
  • Filter context
Returns : boolean

true if item should be shown, false otherwise

import { Injectable } from "@nestjs/common";
import { ParticipantRole, RoleBasedNavigationItem } from "../types";

/**
 * Navigation Filter Context
 * Contains all information needed to filter navigation items
 */
export interface NavigationFilterContext {
  /**
   * User's role (from ParticipantRole enum)
   */
  role: ParticipantRole | string;

  /**
   * Active feature flags (for future expansion)
   */
  featureFlags?: string[];

  /**
   * User permissions (for future expansion)
   */
  permissions?: string[];

  /**
   * Additional context data
   */
  metadata?: Record<string, unknown>;
}

/**
 * Navigation Filter Service
 * Filters navigation items based on user role, permissions, and feature flags
 *
 * This service is designed to be extensible:
 * - Add new filter criteria by extending NavigationFilterContext
 * - Implement custom filtering logic in filterItems method
 * - Support nested navigation items (children)
 */
@Injectable()
export class NavigationFilterService {
  /**
   * Filter navigation items based on context
   * @param items - Array of navigation items to filter
   * @param context - Filter context containing role, permissions, etc.
   * @returns Filtered array of navigation items
   */
  filterItems(items: RoleBasedNavigationItem[], context: NavigationFilterContext): RoleBasedNavigationItem[] {
    return items
      .filter((item) => this.shouldShowItem(item, context))
      .map((item) => this.processItem(item, context));
  }

  /**
   * Determine if an item should be shown based on filter context
   * @param item - Navigation item to check
   * @param context - Filter context
   * @returns true if item should be shown, false otherwise
   */
  private shouldShowItem(item: RoleBasedNavigationItem, context: NavigationFilterContext): boolean {
    // Check if item is explicitly hidden
    if (item.hidden) {
      return false;
    }

    // Check role-based access
    if (item.allowedRoles && item.allowedRoles.length > 0) {
      if (!this.hasRequiredRole(context.role, item.allowedRoles)) {
        return false;
      }
    }

    // Check feature flags (if specified)
    if (item.featureFlags && item.featureFlags.length > 0) {
      if (!this.hasRequiredFeatureFlags(context.featureFlags || [], item.featureFlags)) {
        return false;
      }
    }

    // Check permissions (if specified) - for future expansion
    if (item.permissions && item.permissions.length > 0) {
      if (!this.hasRequiredPermissions(context.permissions || [], item.permissions)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Process a navigation item (filter children, clean metadata, etc.)
   * @param item - Navigation item to process
   * @param context - Filter context
   * @returns Processed navigation item
   */
  private processItem(item: RoleBasedNavigationItem, context: NavigationFilterContext): RoleBasedNavigationItem {
    // Create a copy to avoid mutating the original
    const processedItem = { ...item };

    // Recursively filter children if they exist
    if (processedItem.children && processedItem.children.length > 0) {
      processedItem.children = this.filterItems(processedItem.children, context);

      // If all children are filtered out, mark parent as hidden (optional)
      // Uncomment if you want to hide parent items with no visible children
      // if (processedItem.children.length === 0) {
      //   processedItem.hidden = true;
      // }
    }

    // Remove internal metadata that shouldn't be sent to frontend
    // (Keep allowedRoles etc. for debugging, or remove them here)
    // delete processedItem.allowedRoles;
    // delete processedItem.featureFlags;
    // delete processedItem.permissions;

    return processedItem;
  }

  /**
   * Check if user has required role
   * @param userRole - User's role
   * @param allowedRoles - Array of allowed roles
   * @returns true if user has at least one of the allowed roles
   */
  private hasRequiredRole(userRole: ParticipantRole | string, allowedRoles: ParticipantRole[]): boolean {
    return allowedRoles.includes(userRole as ParticipantRole);
  }

  /**
   * Check if user has required feature flags
   * @param userFlags - User's active feature flags
   * @param requiredFlags - Required feature flags (at least one must match)
   * @returns true if user has at least one required feature flag
   */
  private hasRequiredFeatureFlags(userFlags: string[], requiredFlags: string[]): boolean {
    if (requiredFlags.length === 0) return true;
    return requiredFlags.some((flag) => userFlags.includes(flag));
  }

  /**
   * Check if user has required permissions
   * @param userPermissions - User's permissions
   * @param requiredPermissions - Required permissions (at least one must match)
   * @returns true if user has at least one required permission
   */
  private hasRequiredPermissions(userPermissions: string[], requiredPermissions: string[]): boolean {
    if (requiredPermissions.length === 0) return true;
    return requiredPermissions.some((permission) => userPermissions.includes(permission));
  }

  /**
   * Get navigation items for a specific role (convenience method)
   * @param items - Array of navigation items
   * @param role - User role
   * @returns Filtered navigation items for the role
   */
  getItemsForRole(items: RoleBasedNavigationItem[], role: ParticipantRole | string): RoleBasedNavigationItem[] {
    return this.filterItems(items, { role });
  }
}

results matching ""

    No results matching ""