/**
* Capacity Service
* Manages Fabric capacity administration
*/
import * as vscode from 'vscode';
import { FabricApiService } from '../core/api/fabric-api-service';
export interface Capacity {
id: string;
displayName: string;
sku: string;
region: string;
state: 'Active' | 'Paused' | 'Deleting' | 'Deleted' | 'Provisioning' | 'ProvisionFailed';
admins: string[];
tenantId?: string;
capacityUserAccessRight?: 'Admin' | 'Contributor' | 'Member' | 'None';
}
export interface CapacityWorkload {
name: string;
state: 'Enabled' | 'Disabled';
maxMemoryPercentageSetByUser?: number;
}
export interface CapacityUsage {
capacityId: string;
timestamp: Date;
cuUsage: number;
cuLimit: number;
cuUtilizationPercent: number;
memoryUsageBytes: number;
memoryLimitBytes: number;
memoryUtilizationPercent: number;
activeOperations: number;
queuedOperations: number;
}
export interface CapacityWorkspace {
workspaceId: string;
workspaceName: string;
assignedAt?: Date;
assignedBy?: string;
}
export interface CapacityAlert {
id: string;
capacityId: string;
alertType: 'HighUsage' | 'Throttling' | 'Error' | 'Warning';
message: string;
severity: 'Critical' | 'Warning' | 'Info';
timestamp: Date;
resolved: boolean;
resolvedAt?: Date;
}
export interface CapacitySettings {
capacityId: string;
notificationsEnabled: boolean;
autoScaleEnabled: boolean;
autoScaleMinSku?: string;
autoScaleMaxSku?: string;
autoPauseEnabled: boolean;
autoPauseDelayMinutes?: number;
}
export class CapacityService {
private api: FabricApiService;
private usagePollingTimer: ReturnType<typeof setTimeout> | null = null;
private usageCallbacks: Map<string, (usage: CapacityUsage) => void> = new Map();
constructor() {
this.api = FabricApiService.getInstance();
}
async getCapacities(): Promise<Capacity[]> {
try {
const result = await this.api.get<{ value: Capacity[] }>('/capacities');
return result?.value || [];
} catch (error) {
console.error('Failed to get capacities:', error);
return [];
}
}
async getCapacity(capacityId: string): Promise<Capacity | null> {
try {
return await this.api.get<Capacity>(`/capacities/${capacityId}`);
} catch (error) {
console.error('Failed to get capacity:', error);
return null;
}
}
async getCapacityWorkloads(capacityId: string): Promise<CapacityWorkload[]> {
try {
const result = await this.api.get<{ value: CapacityWorkload[] }>(
`/capacities/${capacityId}/workloads`
);
return result?.value || [];
} catch (error) {
console.error('Failed to get capacity workloads:', error);
return [];
}
}
async updateWorkload(capacityId: string, workloadName: string, updates: Partial<CapacityWorkload>): Promise<boolean> {
try {
await this.api.patch(
`/capacities/${capacityId}/workloads/${workloadName}`,
updates
);
return true;
} catch (error) {
console.error('Failed to update workload:', error);
return false;
}
}
async getCapacityUsage(capacityId: string): Promise<CapacityUsage | null> {
try {
return await this.api.get<CapacityUsage>(
`/capacities/${capacityId}/usage`
);
} catch (error) {
console.error('Failed to get capacity usage:', error);
return null;
}
}
async getCapacityUsageHistory(capacityId: string, hours: number = 24): Promise<CapacityUsage[]> {
try {
const endTime = new Date();
const startTime = new Date();
startTime.setHours(startTime.getHours() - hours);
const result = await this.api.get<{ value: CapacityUsage[] }>(
`/capacities/${capacityId}/usageHistory`,
{
startDateTime: startTime.toISOString(),
endDateTime: endTime.toISOString()
}
);
return result?.value || [];
} catch (error) {
console.error('Failed to get usage history:', error);
return [];
}
}
async getAssignedWorkspaces(capacityId: string): Promise<CapacityWorkspace[]> {
try {
const result = await this.api.get<{ value: CapacityWorkspace[] }>(
`/capacities/${capacityId}/workspaces`
);
return result?.value || [];
} catch (error) {
console.error('Failed to get assigned workspaces:', error);
return [];
}
}
async assignWorkspace(capacityId: string, workspaceId: string): Promise<boolean> {
try {
await this.api.post(
`/capacities/${capacityId}/assignWorkspace`,
{ workspaceId }
);
return true;
} catch (error) {
console.error('Failed to assign workspace:', error);
return false;
}
}
async unassignWorkspace(capacityId: string, workspaceId: string): Promise<boolean> {
try {
await this.api.post(
`/capacities/${capacityId}/unassignWorkspace`,
{ workspaceId }
);
return true;
} catch (error) {
console.error('Failed to unassign workspace:', error);
return false;
}
}
async getCapacityAlerts(capacityId: string): Promise<CapacityAlert[]> {
try {
const result = await this.api.get<{ value: CapacityAlert[] }>(
`/capacities/${capacityId}/alerts`
);
return result?.value || [];
} catch (error) {
console.error('Failed to get alerts:', error);
return [];
}
}
async dismissAlert(capacityId: string, alertId: string): Promise<boolean> {
try {
await this.api.post(
`/capacities/${capacityId}/alerts/${alertId}/dismiss`,
{}
);
return true;
} catch (error) {
console.error('Failed to dismiss alert:', error);
return false;
}
}
async getCapacitySettings(capacityId: string): Promise<CapacitySettings | null> {
try {
return await this.api.get<CapacitySettings>(
`/capacities/${capacityId}/settings`
);
} catch (error) {
console.error('Failed to get capacity settings:', error);
return null;
}
}
async updateCapacitySettings(capacityId: string, settings: Partial<CapacitySettings>): Promise<boolean> {
try {
await this.api.patch(
`/capacities/${capacityId}/settings`,
settings
);
return true;
} catch (error) {
console.error('Failed to update capacity settings:', error);
return false;
}
}
async pauseCapacity(capacityId: string): Promise<boolean> {
try {
await this.api.post(`/capacities/${capacityId}/suspend`, {});
return true;
} catch (error) {
console.error('Failed to pause capacity:', error);
return false;
}
}
async resumeCapacity(capacityId: string): Promise<boolean> {
try {
await this.api.post(`/capacities/${capacityId}/resume`, {});
return true;
} catch (error) {
console.error('Failed to resume capacity:', error);
return false;
}
}
async scaleCapacity(capacityId: string, targetSku: string): Promise<boolean> {
try {
await this.api.patch(`/capacities/${capacityId}`, { sku: targetSku });
return true;
} catch (error) {
console.error('Failed to scale capacity:', error);
return false;
}
}
startUsageMonitoring(capacityId: string, callback: (usage: CapacityUsage) => void, intervalMs: number = 30000): void {
this.usageCallbacks.set(capacityId, callback);
const poll = async () => {
const usage = await this.getCapacityUsage(capacityId);
if (usage) {
const cb = this.usageCallbacks.get(capacityId);
cb?.(usage);
}
if (this.usageCallbacks.has(capacityId)) {
this.usagePollingTimer = setTimeout(poll, intervalMs);
}
};
poll();
}
stopUsageMonitoring(capacityId: string): void {
this.usageCallbacks.delete(capacityId);
if (this.usageCallbacks.size === 0 && this.usagePollingTimer) {
clearTimeout(this.usagePollingTimer);
this.usagePollingTimer = null;
}
}
getStateIcon(state: string): string {
const icons: Record<string, string> = {
'Active': '$(pass)',
'Paused': '$(debug-pause)',
'Deleting': '$(trash)',
'Deleted': '$(circle-slash)',
'Provisioning': '$(sync~spin)',
'ProvisionFailed': '$(error)'
};
return icons[state] || '$(question)';
}
getAlertIcon(severity: string): string {
const icons: Record<string, string> = {
'Critical': '$(error)',
'Warning': '$(warning)',
'Info': '$(info)'
};
return icons[severity] || '$(bell)';
}
formatUsagePercent(percent: number): string {
return `${Math.round(percent)}%`;
}
getUsageColor(percent: number): string {
if (percent >= 90) {
return 'errorForeground';
} else if (percent >= 70) {
return 'warningForeground';
}
return 'foreground';
}
getSkuDisplayName(sku: string): string {
const skuNames: Record<string, string> = {
'F2': 'F2 (2 CU)',
'F4': 'F4 (4 CU)',
'F8': 'F8 (8 CU)',
'F16': 'F16 (16 CU)',
'F32': 'F32 (32 CU)',
'F64': 'F64 (64 CU)',
'F128': 'F128 (128 CU)',
'F256': 'F256 (256 CU)',
'F512': 'F512 (512 CU)',
'F1024': 'F1024 (1024 CU)',
'F2048': 'F2048 (2048 CU)'
};
return skuNames[sku] || sku;
}
dispose(): void {
if (this.usagePollingTimer) {
clearTimeout(this.usagePollingTimer);
this.usagePollingTimer = null;
}
this.usageCallbacks.clear();
}
}