All files / src/services WidgetAvailability.ts

100% Statements 31/31
100% Branches 12/12
100% Functions 5/5
100% Lines 29/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 1121x 1x               1x 1x                                                                               197x   197x 197x 167x   167x   1x       166x 166x 6x       160x 93x     67x           67x 67x   67x     30x         4x   116x         4x       173x   171x 171x   167x 167x   167x      
import { WorkItemWidgetType, WorkItemWidgetTypes } from "../graphql/workItems";
import { ConnectionManager } from "./ConnectionManager";
import { GitLabTier } from "./GitLabVersionDetector";
 
interface WidgetRequirement {
  tier: GitLabTier | "free";
  minVersion: number;
}
 
export class WidgetAvailability {
  private static widgetRequirements: Record<WorkItemWidgetType, WidgetRequirement> = {
    // Free tier widgets (available to all)
    [WorkItemWidgetTypes.ASSIGNEES]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.DESCRIPTION]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.HIERARCHY]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.LABELS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.MILESTONE]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.NOTES]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.START_AND_DUE_DATE]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.STATUS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.NOTIFICATIONS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.CURRENT_USER_TODOS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.AWARD_EMOJI]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.PARTICIPANTS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.DESIGNS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.DEVELOPMENT]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.TIME_TRACKING]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.ERROR_TRACKING]: { tier: "free", minVersion: 15.0 },
 
    // Premium tier widgets
    [WorkItemWidgetTypes.WEIGHT]: { tier: "premium", minVersion: 15.0 },
    [WorkItemWidgetTypes.ITERATION]: { tier: "premium", minVersion: 15.0 },
    [WorkItemWidgetTypes.LINKED_ITEMS]: { tier: "premium", minVersion: 15.0 },
    [WorkItemWidgetTypes.CRM_CONTACTS]: { tier: "premium", minVersion: 16.0 },
    [WorkItemWidgetTypes.EMAIL_PARTICIPANTS]: { tier: "premium", minVersion: 16.0 },
    [WorkItemWidgetTypes.LINKED_RESOURCES]: { tier: "premium", minVersion: 16.5 },
 
    // Ultimate tier widgets
    [WorkItemWidgetTypes.HEALTH_STATUS]: { tier: "ultimate", minVersion: 15.0 },
    [WorkItemWidgetTypes.CUSTOM_FIELDS]: { tier: "ultimate", minVersion: 17.0 },
    [WorkItemWidgetTypes.VULNERABILITIES]: { tier: "ultimate", minVersion: 15.0 },
 
    // Legacy widgets (may not be available)
    [WorkItemWidgetTypes.PROGRESS]: { tier: "free", minVersion: 15.0 },
    [WorkItemWidgetTypes.REQUIREMENT_LEGACY]: { tier: "ultimate", minVersion: 13.1 },
    [WorkItemWidgetTypes.TEST_REPORTS]: { tier: "ultimate", minVersion: 13.6 },
    [WorkItemWidgetTypes.COLOR]: { tier: "free", minVersion: 15.0 },
  };
 
  public static isWidgetAvailable(widget: WorkItemWidgetType): boolean {
    const connectionManager = ConnectionManager.getInstance();
 
    try {
      const instanceInfo = connectionManager.getInstanceInfo();
      const requirement = this.widgetRequirements[widget];
 
      if (!requirement) {
        // Unknown widget, assume not available
        return false;
      }
 
      // Check version requirement
      const version = this.parseVersion(instanceInfo.version);
      if (version < requirement.minVersion) {
        return false;
      }
 
      // Check tier requirement
      if (requirement.tier === "free") {
        return true; // Available to all tiers
      }
 
      const tierHierarchy: Record<GitLabTier, number> = {
        free: 0,
        premium: 1,
        ultimate: 2,
      };
 
      const requiredTierLevel = tierHierarchy[requirement.tier as GitLabTier];
      const actualTierLevel = tierHierarchy[instanceInfo.tier];
 
      return actualTierLevel >= requiredTierLevel;
    } catch {
      // If connection not initialized, assume widget not available
      return false;
    }
  }
 
  public static getAvailableWidgets(): WorkItemWidgetType[] {
    return Object.values(WorkItemWidgetTypes).filter(
      (widget): widget is WorkItemWidgetType =>
        typeof widget === "string" && this.isWidgetAvailable(widget as WorkItemWidgetType)
    );
  }
 
  public static getWidgetRequirement(widget: WorkItemWidgetType): WidgetRequirement | undefined {
    return this.widgetRequirements[widget];
  }
 
  private static parseVersion(version: string): number {
    if (version === "unknown") return 0;
 
    const match = version.match(/^(\d+)\.(\d+)/);
    if (!match) return 0;
 
    const major = parseInt(match[1], 10);
    const minor = parseInt(match[2], 10);
 
    return major + minor / 10;
  }
}